1113db2ddSJeff Roberson /*- 2113db2ddSJeff Roberson * Copyright 2009, 2010 Jeffrey W. Roberson <jeff@FreeBSD.org> 3113db2ddSJeff Roberson * All rights reserved. 4113db2ddSJeff Roberson * 5113db2ddSJeff Roberson * Redistribution and use in source and binary forms, with or without 6113db2ddSJeff Roberson * modification, are permitted provided that the following conditions 7113db2ddSJeff Roberson * are met: 8113db2ddSJeff Roberson * 1. Redistributions of source code must retain the above copyright 9113db2ddSJeff Roberson * notice, this list of conditions and the following disclaimer. 10113db2ddSJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 11113db2ddSJeff Roberson * notice, this list of conditions and the following disclaimer in the 12113db2ddSJeff Roberson * documentation and/or other materials provided with the distribution. 13113db2ddSJeff Roberson * 14113db2ddSJeff Roberson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15113db2ddSJeff Roberson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16113db2ddSJeff Roberson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17113db2ddSJeff Roberson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18113db2ddSJeff Roberson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19113db2ddSJeff Roberson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20113db2ddSJeff Roberson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21113db2ddSJeff Roberson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22113db2ddSJeff Roberson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23113db2ddSJeff Roberson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24113db2ddSJeff Roberson * SUCH DAMAGE. 25113db2ddSJeff Roberson */ 26113db2ddSJeff Roberson 27113db2ddSJeff Roberson #include <sys/cdefs.h> 28113db2ddSJeff Roberson __FBSDID("$FreeBSD$"); 29113db2ddSJeff Roberson 30113db2ddSJeff Roberson #include <sys/param.h> 310947d19aSKonstantin Belousov #include <sys/disk.h> 32113db2ddSJeff Roberson #include <sys/disklabel.h> 33113db2ddSJeff Roberson #include <sys/mount.h> 34113db2ddSJeff Roberson #include <sys/stat.h> 35113db2ddSJeff Roberson 36113db2ddSJeff Roberson #include <ufs/ufs/ufsmount.h> 37113db2ddSJeff Roberson #include <ufs/ufs/dinode.h> 38113db2ddSJeff Roberson #include <ufs/ufs/dir.h> 39113db2ddSJeff Roberson #include <ufs/ffs/fs.h> 40113db2ddSJeff Roberson 417649cb00SKirk McKusick #include <assert.h> 427649cb00SKirk McKusick #include <err.h> 43edad6026SXin LI #include <setjmp.h> 44edad6026SXin LI #include <stdarg.h> 45113db2ddSJeff Roberson #include <stdio.h> 46113db2ddSJeff Roberson #include <stdlib.h> 47113db2ddSJeff Roberson #include <stdint.h> 48113db2ddSJeff Roberson #include <libufs.h> 49113db2ddSJeff Roberson #include <string.h> 50113db2ddSJeff Roberson #include <strings.h> 51edad6026SXin LI #include <sysexits.h> 527649cb00SKirk McKusick #include <time.h> 53113db2ddSJeff Roberson 54113db2ddSJeff Roberson #include "fsck.h" 55113db2ddSJeff Roberson 56113db2ddSJeff Roberson #define DOTDOT_OFFSET DIRECTSIZ(1) 57113db2ddSJeff Roberson #define SUJ_HASHSIZE 2048 58113db2ddSJeff Roberson #define SUJ_HASHMASK (SUJ_HASHSIZE - 1) 59113db2ddSJeff Roberson #define SUJ_HASH(x) ((x * 2654435761) & SUJ_HASHMASK) 60113db2ddSJeff Roberson 61113db2ddSJeff Roberson struct suj_seg { 62113db2ddSJeff Roberson TAILQ_ENTRY(suj_seg) ss_next; 63113db2ddSJeff Roberson struct jsegrec ss_rec; 64113db2ddSJeff Roberson uint8_t *ss_blk; 65113db2ddSJeff Roberson }; 66113db2ddSJeff Roberson 67113db2ddSJeff Roberson struct suj_rec { 68113db2ddSJeff Roberson TAILQ_ENTRY(suj_rec) sr_next; 69113db2ddSJeff Roberson union jrec *sr_rec; 70113db2ddSJeff Roberson }; 71113db2ddSJeff Roberson TAILQ_HEAD(srechd, suj_rec); 72113db2ddSJeff Roberson 73113db2ddSJeff Roberson struct suj_ino { 74113db2ddSJeff Roberson LIST_ENTRY(suj_ino) si_next; 75113db2ddSJeff Roberson struct srechd si_recs; 76113db2ddSJeff Roberson struct srechd si_newrecs; 77113db2ddSJeff Roberson struct srechd si_movs; 78113db2ddSJeff Roberson struct jtrncrec *si_trunc; 79113db2ddSJeff Roberson ino_t si_ino; 80113db2ddSJeff Roberson char si_skipparent; 81113db2ddSJeff Roberson char si_hasrecs; 82113db2ddSJeff Roberson char si_blkadj; 83113db2ddSJeff Roberson char si_linkadj; 84113db2ddSJeff Roberson int si_mode; 85113db2ddSJeff Roberson nlink_t si_nlinkadj; 86113db2ddSJeff Roberson nlink_t si_nlink; 87113db2ddSJeff Roberson nlink_t si_dotlinks; 88113db2ddSJeff Roberson }; 89113db2ddSJeff Roberson LIST_HEAD(inohd, suj_ino); 90113db2ddSJeff Roberson 91113db2ddSJeff Roberson struct suj_blk { 92113db2ddSJeff Roberson LIST_ENTRY(suj_blk) sb_next; 93113db2ddSJeff Roberson struct srechd sb_recs; 94113db2ddSJeff Roberson ufs2_daddr_t sb_blk; 95113db2ddSJeff Roberson }; 96113db2ddSJeff Roberson LIST_HEAD(blkhd, suj_blk); 97113db2ddSJeff Roberson 98113db2ddSJeff Roberson struct data_blk { 99113db2ddSJeff Roberson LIST_ENTRY(data_blk) db_next; 100113db2ddSJeff Roberson uint8_t *db_buf; 101113db2ddSJeff Roberson ufs2_daddr_t db_blk; 102113db2ddSJeff Roberson int db_size; 103113db2ddSJeff Roberson int db_dirty; 104113db2ddSJeff Roberson }; 105113db2ddSJeff Roberson 106113db2ddSJeff Roberson struct ino_blk { 107113db2ddSJeff Roberson LIST_ENTRY(ino_blk) ib_next; 108113db2ddSJeff Roberson uint8_t *ib_buf; 109113db2ddSJeff Roberson int ib_dirty; 110113db2ddSJeff Roberson ufs2_daddr_t ib_blk; 111113db2ddSJeff Roberson }; 112113db2ddSJeff Roberson LIST_HEAD(iblkhd, ino_blk); 113113db2ddSJeff Roberson 114113db2ddSJeff Roberson struct suj_cg { 115113db2ddSJeff Roberson LIST_ENTRY(suj_cg) sc_next; 116113db2ddSJeff Roberson struct blkhd sc_blkhash[SUJ_HASHSIZE]; 117113db2ddSJeff Roberson struct inohd sc_inohash[SUJ_HASHSIZE]; 118113db2ddSJeff Roberson struct iblkhd sc_iblkhash[SUJ_HASHSIZE]; 119113db2ddSJeff Roberson struct ino_blk *sc_lastiblk; 120113db2ddSJeff Roberson struct suj_ino *sc_lastino; 121113db2ddSJeff Roberson struct suj_blk *sc_lastblk; 122113db2ddSJeff Roberson uint8_t *sc_cgbuf; 123113db2ddSJeff Roberson struct cg *sc_cgp; 124113db2ddSJeff Roberson int sc_dirty; 125113db2ddSJeff Roberson int sc_cgx; 126113db2ddSJeff Roberson }; 127113db2ddSJeff Roberson 128113db2ddSJeff Roberson LIST_HEAD(cghd, suj_cg) cghash[SUJ_HASHSIZE]; 129113db2ddSJeff Roberson LIST_HEAD(dblkhd, data_blk) dbhash[SUJ_HASHSIZE]; 130113db2ddSJeff Roberson struct suj_cg *lastcg; 131113db2ddSJeff Roberson struct data_blk *lastblk; 132113db2ddSJeff Roberson 133113db2ddSJeff Roberson TAILQ_HEAD(seghd, suj_seg) allsegs; 134113db2ddSJeff Roberson uint64_t oldseq; 135113db2ddSJeff Roberson static struct uufsd *disk = NULL; 136113db2ddSJeff Roberson static struct fs *fs = NULL; 137113db2ddSJeff Roberson ino_t sujino; 138113db2ddSJeff Roberson 139113db2ddSJeff Roberson /* 140113db2ddSJeff Roberson * Summary statistics. 141113db2ddSJeff Roberson */ 142113db2ddSJeff Roberson uint64_t freefrags; 143113db2ddSJeff Roberson uint64_t freeblocks; 144113db2ddSJeff Roberson uint64_t freeinos; 145113db2ddSJeff Roberson uint64_t freedir; 146113db2ddSJeff Roberson uint64_t jbytes; 147113db2ddSJeff Roberson uint64_t jrecs; 148113db2ddSJeff Roberson 149edad6026SXin LI static jmp_buf jmpbuf; 150edad6026SXin LI 151113db2ddSJeff Roberson typedef void (*ino_visitor)(ino_t, ufs_lbn_t, ufs2_daddr_t, int); 152edad6026SXin LI static void err_suj(const char *, ...) __dead2; 153113db2ddSJeff Roberson static void ino_trunc(ino_t, off_t); 154113db2ddSJeff Roberson static void ino_decr(ino_t); 155113db2ddSJeff Roberson static void ino_adjust(struct suj_ino *); 156113db2ddSJeff Roberson static void ino_build(struct suj_ino *); 157113db2ddSJeff Roberson static int blk_isfree(ufs2_daddr_t); 158113db2ddSJeff Roberson 159113db2ddSJeff Roberson static void * 160113db2ddSJeff Roberson errmalloc(size_t n) 161113db2ddSJeff Roberson { 162113db2ddSJeff Roberson void *a; 163113db2ddSJeff Roberson 164*81fbded2SKirk McKusick a = Malloc(n); 165113db2ddSJeff Roberson if (a == NULL) 166edad6026SXin LI err(EX_OSERR, "malloc(%zu)", n); 167113db2ddSJeff Roberson return (a); 168113db2ddSJeff Roberson } 169113db2ddSJeff Roberson 170113db2ddSJeff Roberson /* 171edad6026SXin LI * When hit a fatal error in journalling check, print out 172edad6026SXin LI * the error and then offer to fallback to normal fsck. 173edad6026SXin LI */ 174edad6026SXin LI static void 175edad6026SXin LI err_suj(const char * restrict fmt, ...) 176edad6026SXin LI { 177edad6026SXin LI va_list ap; 178edad6026SXin LI 179edad6026SXin LI if (preen) 180edad6026SXin LI (void)fprintf(stdout, "%s: ", cdevname); 181edad6026SXin LI 182edad6026SXin LI va_start(ap, fmt); 183edad6026SXin LI (void)vfprintf(stdout, fmt, ap); 184edad6026SXin LI va_end(ap); 185edad6026SXin LI 186edad6026SXin LI longjmp(jmpbuf, -1); 187edad6026SXin LI } 188edad6026SXin LI 189edad6026SXin LI /* 190113db2ddSJeff Roberson * Open the given provider, load superblock. 191113db2ddSJeff Roberson */ 192113db2ddSJeff Roberson static void 193113db2ddSJeff Roberson opendisk(const char *devnam) 194113db2ddSJeff Roberson { 195113db2ddSJeff Roberson if (disk != NULL) 196113db2ddSJeff Roberson return; 197*81fbded2SKirk McKusick disk = Malloc(sizeof(*disk)); 198113db2ddSJeff Roberson if (disk == NULL) 199edad6026SXin LI err(EX_OSERR, "malloc(%zu)", sizeof(*disk)); 200113db2ddSJeff Roberson if (ufs_disk_fillout(disk, devnam) == -1) { 201edad6026SXin LI err(EX_OSERR, "ufs_disk_fillout(%s) failed: %s", devnam, 202113db2ddSJeff Roberson disk->d_error); 203113db2ddSJeff Roberson } 204113db2ddSJeff Roberson fs = &disk->d_fs; 2050947d19aSKonstantin Belousov if (real_dev_bsize == 0 && ioctl(disk->d_fd, DIOCGSECTORSIZE, 2060947d19aSKonstantin Belousov &real_dev_bsize) == -1) 2070947d19aSKonstantin Belousov real_dev_bsize = secsize; 2080947d19aSKonstantin Belousov if (debug) 2096f100596SKonstantin Belousov printf("dev_bsize %u\n", real_dev_bsize); 210113db2ddSJeff Roberson } 211113db2ddSJeff Roberson 212113db2ddSJeff Roberson /* 213113db2ddSJeff Roberson * Mark file system as clean, write the super-block back, close the disk. 214113db2ddSJeff Roberson */ 215113db2ddSJeff Roberson static void 216113db2ddSJeff Roberson closedisk(const char *devnam) 217113db2ddSJeff Roberson { 218113db2ddSJeff Roberson struct csum *cgsum; 219113db2ddSJeff Roberson int i; 220113db2ddSJeff Roberson 221113db2ddSJeff Roberson /* 222113db2ddSJeff Roberson * Recompute the fs summary info from correct cs summaries. 223113db2ddSJeff Roberson */ 224113db2ddSJeff Roberson bzero(&fs->fs_cstotal, sizeof(struct csum_total)); 225113db2ddSJeff Roberson for (i = 0; i < fs->fs_ncg; i++) { 226113db2ddSJeff Roberson cgsum = &fs->fs_cs(fs, i); 227113db2ddSJeff Roberson fs->fs_cstotal.cs_nffree += cgsum->cs_nffree; 228113db2ddSJeff Roberson fs->fs_cstotal.cs_nbfree += cgsum->cs_nbfree; 229113db2ddSJeff Roberson fs->fs_cstotal.cs_nifree += cgsum->cs_nifree; 230113db2ddSJeff Roberson fs->fs_cstotal.cs_ndir += cgsum->cs_ndir; 231113db2ddSJeff Roberson } 232113db2ddSJeff Roberson fs->fs_pendinginodes = 0; 233113db2ddSJeff Roberson fs->fs_pendingblocks = 0; 234113db2ddSJeff Roberson fs->fs_clean = 1; 235113db2ddSJeff Roberson fs->fs_time = time(NULL); 236113db2ddSJeff Roberson fs->fs_mtime = time(NULL); 237113db2ddSJeff Roberson if (sbwrite(disk, 0) == -1) 238edad6026SXin LI err(EX_OSERR, "sbwrite(%s)", devnam); 239113db2ddSJeff Roberson if (ufs_disk_close(disk) == -1) 240edad6026SXin LI err(EX_OSERR, "ufs_disk_close(%s)", devnam); 241113db2ddSJeff Roberson free(disk); 242113db2ddSJeff Roberson disk = NULL; 243113db2ddSJeff Roberson fs = NULL; 244113db2ddSJeff Roberson } 245113db2ddSJeff Roberson 246113db2ddSJeff Roberson /* 247113db2ddSJeff Roberson * Lookup a cg by number in the hash so we can keep track of which cgs 248113db2ddSJeff Roberson * need stats rebuilt. 249113db2ddSJeff Roberson */ 250113db2ddSJeff Roberson static struct suj_cg * 251113db2ddSJeff Roberson cg_lookup(int cgx) 252113db2ddSJeff Roberson { 253113db2ddSJeff Roberson struct cghd *hd; 254113db2ddSJeff Roberson struct suj_cg *sc; 255113db2ddSJeff Roberson 256edad6026SXin LI if (cgx < 0 || cgx >= fs->fs_ncg) 257edad6026SXin LI err_suj("Bad cg number %d\n", cgx); 258113db2ddSJeff Roberson if (lastcg && lastcg->sc_cgx == cgx) 259113db2ddSJeff Roberson return (lastcg); 260113db2ddSJeff Roberson hd = &cghash[SUJ_HASH(cgx)]; 261113db2ddSJeff Roberson LIST_FOREACH(sc, hd, sc_next) 262113db2ddSJeff Roberson if (sc->sc_cgx == cgx) { 263113db2ddSJeff Roberson lastcg = sc; 264113db2ddSJeff Roberson return (sc); 265113db2ddSJeff Roberson } 266113db2ddSJeff Roberson sc = errmalloc(sizeof(*sc)); 267113db2ddSJeff Roberson bzero(sc, sizeof(*sc)); 268113db2ddSJeff Roberson sc->sc_cgbuf = errmalloc(fs->fs_bsize); 269113db2ddSJeff Roberson sc->sc_cgp = (struct cg *)sc->sc_cgbuf; 270113db2ddSJeff Roberson sc->sc_cgx = cgx; 271113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, sc, sc_next); 272113db2ddSJeff Roberson if (bread(disk, fsbtodb(fs, cgtod(fs, sc->sc_cgx)), sc->sc_cgbuf, 273113db2ddSJeff Roberson fs->fs_bsize) == -1) 274edad6026SXin LI err_suj("Unable to read cylinder group %d\n", sc->sc_cgx); 275113db2ddSJeff Roberson 276113db2ddSJeff Roberson return (sc); 277113db2ddSJeff Roberson } 278113db2ddSJeff Roberson 279113db2ddSJeff Roberson /* 280113db2ddSJeff Roberson * Lookup an inode number in the hash and allocate a suj_ino if it does 281113db2ddSJeff Roberson * not exist. 282113db2ddSJeff Roberson */ 283113db2ddSJeff Roberson static struct suj_ino * 284113db2ddSJeff Roberson ino_lookup(ino_t ino, int creat) 285113db2ddSJeff Roberson { 286113db2ddSJeff Roberson struct suj_ino *sino; 287113db2ddSJeff Roberson struct inohd *hd; 288113db2ddSJeff Roberson struct suj_cg *sc; 289113db2ddSJeff Roberson 290113db2ddSJeff Roberson sc = cg_lookup(ino_to_cg(fs, ino)); 291113db2ddSJeff Roberson if (sc->sc_lastino && sc->sc_lastino->si_ino == ino) 292113db2ddSJeff Roberson return (sc->sc_lastino); 293113db2ddSJeff Roberson hd = &sc->sc_inohash[SUJ_HASH(ino)]; 294113db2ddSJeff Roberson LIST_FOREACH(sino, hd, si_next) 295113db2ddSJeff Roberson if (sino->si_ino == ino) 296113db2ddSJeff Roberson return (sino); 297113db2ddSJeff Roberson if (creat == 0) 298113db2ddSJeff Roberson return (NULL); 299113db2ddSJeff Roberson sino = errmalloc(sizeof(*sino)); 300113db2ddSJeff Roberson bzero(sino, sizeof(*sino)); 301113db2ddSJeff Roberson sino->si_ino = ino; 302113db2ddSJeff Roberson TAILQ_INIT(&sino->si_recs); 303113db2ddSJeff Roberson TAILQ_INIT(&sino->si_newrecs); 304113db2ddSJeff Roberson TAILQ_INIT(&sino->si_movs); 305113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, sino, si_next); 306113db2ddSJeff Roberson 307113db2ddSJeff Roberson return (sino); 308113db2ddSJeff Roberson } 309113db2ddSJeff Roberson 310113db2ddSJeff Roberson /* 311113db2ddSJeff Roberson * Lookup a block number in the hash and allocate a suj_blk if it does 312113db2ddSJeff Roberson * not exist. 313113db2ddSJeff Roberson */ 314113db2ddSJeff Roberson static struct suj_blk * 315113db2ddSJeff Roberson blk_lookup(ufs2_daddr_t blk, int creat) 316113db2ddSJeff Roberson { 317113db2ddSJeff Roberson struct suj_blk *sblk; 318113db2ddSJeff Roberson struct suj_cg *sc; 319113db2ddSJeff Roberson struct blkhd *hd; 320113db2ddSJeff Roberson 321113db2ddSJeff Roberson sc = cg_lookup(dtog(fs, blk)); 322113db2ddSJeff Roberson if (sc->sc_lastblk && sc->sc_lastblk->sb_blk == blk) 323113db2ddSJeff Roberson return (sc->sc_lastblk); 324113db2ddSJeff Roberson hd = &sc->sc_blkhash[SUJ_HASH(fragstoblks(fs, blk))]; 325113db2ddSJeff Roberson LIST_FOREACH(sblk, hd, sb_next) 326113db2ddSJeff Roberson if (sblk->sb_blk == blk) 327113db2ddSJeff Roberson return (sblk); 328113db2ddSJeff Roberson if (creat == 0) 329113db2ddSJeff Roberson return (NULL); 330113db2ddSJeff Roberson sblk = errmalloc(sizeof(*sblk)); 331113db2ddSJeff Roberson bzero(sblk, sizeof(*sblk)); 332113db2ddSJeff Roberson sblk->sb_blk = blk; 333113db2ddSJeff Roberson TAILQ_INIT(&sblk->sb_recs); 334113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, sblk, sb_next); 335113db2ddSJeff Roberson 336113db2ddSJeff Roberson return (sblk); 337113db2ddSJeff Roberson } 338113db2ddSJeff Roberson 339113db2ddSJeff Roberson static struct data_blk * 340113db2ddSJeff Roberson dblk_lookup(ufs2_daddr_t blk) 341113db2ddSJeff Roberson { 342113db2ddSJeff Roberson struct data_blk *dblk; 343113db2ddSJeff Roberson struct dblkhd *hd; 344113db2ddSJeff Roberson 345113db2ddSJeff Roberson hd = &dbhash[SUJ_HASH(fragstoblks(fs, blk))]; 346113db2ddSJeff Roberson if (lastblk && lastblk->db_blk == blk) 347113db2ddSJeff Roberson return (lastblk); 348113db2ddSJeff Roberson LIST_FOREACH(dblk, hd, db_next) 349113db2ddSJeff Roberson if (dblk->db_blk == blk) 350113db2ddSJeff Roberson return (dblk); 351113db2ddSJeff Roberson /* 352113db2ddSJeff Roberson * The inode block wasn't located, allocate a new one. 353113db2ddSJeff Roberson */ 354113db2ddSJeff Roberson dblk = errmalloc(sizeof(*dblk)); 355113db2ddSJeff Roberson bzero(dblk, sizeof(*dblk)); 356113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, dblk, db_next); 357113db2ddSJeff Roberson dblk->db_blk = blk; 358113db2ddSJeff Roberson return (dblk); 359113db2ddSJeff Roberson } 360113db2ddSJeff Roberson 361113db2ddSJeff Roberson static uint8_t * 362113db2ddSJeff Roberson dblk_read(ufs2_daddr_t blk, int size) 363113db2ddSJeff Roberson { 364113db2ddSJeff Roberson struct data_blk *dblk; 365113db2ddSJeff Roberson 366113db2ddSJeff Roberson dblk = dblk_lookup(blk); 367113db2ddSJeff Roberson /* 368113db2ddSJeff Roberson * I doubt size mismatches can happen in practice but it is trivial 369113db2ddSJeff Roberson * to handle. 370113db2ddSJeff Roberson */ 371113db2ddSJeff Roberson if (size != dblk->db_size) { 372113db2ddSJeff Roberson if (dblk->db_buf) 373113db2ddSJeff Roberson free(dblk->db_buf); 374113db2ddSJeff Roberson dblk->db_buf = errmalloc(size); 375113db2ddSJeff Roberson dblk->db_size = size; 376113db2ddSJeff Roberson if (bread(disk, fsbtodb(fs, blk), dblk->db_buf, size) == -1) 377edad6026SXin LI err_suj("Failed to read data block %jd\n", blk); 378113db2ddSJeff Roberson } 379113db2ddSJeff Roberson return (dblk->db_buf); 380113db2ddSJeff Roberson } 381113db2ddSJeff Roberson 382113db2ddSJeff Roberson static void 383113db2ddSJeff Roberson dblk_dirty(ufs2_daddr_t blk) 384113db2ddSJeff Roberson { 385113db2ddSJeff Roberson struct data_blk *dblk; 386113db2ddSJeff Roberson 387113db2ddSJeff Roberson dblk = dblk_lookup(blk); 388113db2ddSJeff Roberson dblk->db_dirty = 1; 389113db2ddSJeff Roberson } 390113db2ddSJeff Roberson 391113db2ddSJeff Roberson static void 392113db2ddSJeff Roberson dblk_write(void) 393113db2ddSJeff Roberson { 394113db2ddSJeff Roberson struct data_blk *dblk; 395113db2ddSJeff Roberson int i; 396113db2ddSJeff Roberson 397113db2ddSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) { 398113db2ddSJeff Roberson LIST_FOREACH(dblk, &dbhash[i], db_next) { 399113db2ddSJeff Roberson if (dblk->db_dirty == 0 || dblk->db_size == 0) 400113db2ddSJeff Roberson continue; 401113db2ddSJeff Roberson if (bwrite(disk, fsbtodb(fs, dblk->db_blk), 402113db2ddSJeff Roberson dblk->db_buf, dblk->db_size) == -1) 403edad6026SXin LI err_suj("Unable to write block %jd\n", 404113db2ddSJeff Roberson dblk->db_blk); 405113db2ddSJeff Roberson } 406113db2ddSJeff Roberson } 407113db2ddSJeff Roberson } 408113db2ddSJeff Roberson 409113db2ddSJeff Roberson static union dinode * 410113db2ddSJeff Roberson ino_read(ino_t ino) 411113db2ddSJeff Roberson { 412113db2ddSJeff Roberson struct ino_blk *iblk; 413113db2ddSJeff Roberson struct iblkhd *hd; 414113db2ddSJeff Roberson struct suj_cg *sc; 415113db2ddSJeff Roberson ufs2_daddr_t blk; 416113db2ddSJeff Roberson int off; 417113db2ddSJeff Roberson 418113db2ddSJeff Roberson blk = ino_to_fsba(fs, ino); 419113db2ddSJeff Roberson sc = cg_lookup(ino_to_cg(fs, ino)); 420113db2ddSJeff Roberson iblk = sc->sc_lastiblk; 421113db2ddSJeff Roberson if (iblk && iblk->ib_blk == blk) 422113db2ddSJeff Roberson goto found; 423113db2ddSJeff Roberson hd = &sc->sc_iblkhash[SUJ_HASH(fragstoblks(fs, blk))]; 424113db2ddSJeff Roberson LIST_FOREACH(iblk, hd, ib_next) 425113db2ddSJeff Roberson if (iblk->ib_blk == blk) 426113db2ddSJeff Roberson goto found; 427113db2ddSJeff Roberson /* 428113db2ddSJeff Roberson * The inode block wasn't located, allocate a new one. 429113db2ddSJeff Roberson */ 430113db2ddSJeff Roberson iblk = errmalloc(sizeof(*iblk)); 431113db2ddSJeff Roberson bzero(iblk, sizeof(*iblk)); 432113db2ddSJeff Roberson iblk->ib_buf = errmalloc(fs->fs_bsize); 433113db2ddSJeff Roberson iblk->ib_blk = blk; 434113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, iblk, ib_next); 435113db2ddSJeff Roberson if (bread(disk, fsbtodb(fs, blk), iblk->ib_buf, fs->fs_bsize) == -1) 436edad6026SXin LI err_suj("Failed to read inode block %jd\n", blk); 437113db2ddSJeff Roberson found: 438113db2ddSJeff Roberson sc->sc_lastiblk = iblk; 439113db2ddSJeff Roberson off = ino_to_fsbo(fs, ino); 440113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 441113db2ddSJeff Roberson return (union dinode *)&((struct ufs1_dinode *)iblk->ib_buf)[off]; 442113db2ddSJeff Roberson else 443113db2ddSJeff Roberson return (union dinode *)&((struct ufs2_dinode *)iblk->ib_buf)[off]; 444113db2ddSJeff Roberson } 445113db2ddSJeff Roberson 446113db2ddSJeff Roberson static void 447113db2ddSJeff Roberson ino_dirty(ino_t ino) 448113db2ddSJeff Roberson { 449113db2ddSJeff Roberson struct ino_blk *iblk; 450113db2ddSJeff Roberson struct iblkhd *hd; 451113db2ddSJeff Roberson struct suj_cg *sc; 452113db2ddSJeff Roberson ufs2_daddr_t blk; 453113db2ddSJeff Roberson 454113db2ddSJeff Roberson blk = ino_to_fsba(fs, ino); 455113db2ddSJeff Roberson sc = cg_lookup(ino_to_cg(fs, ino)); 456113db2ddSJeff Roberson iblk = sc->sc_lastiblk; 457113db2ddSJeff Roberson if (iblk && iblk->ib_blk == blk) { 458113db2ddSJeff Roberson iblk->ib_dirty = 1; 459113db2ddSJeff Roberson return; 460113db2ddSJeff Roberson } 461113db2ddSJeff Roberson hd = &sc->sc_iblkhash[SUJ_HASH(fragstoblks(fs, blk))]; 462113db2ddSJeff Roberson LIST_FOREACH(iblk, hd, ib_next) { 463113db2ddSJeff Roberson if (iblk->ib_blk == blk) { 464113db2ddSJeff Roberson iblk->ib_dirty = 1; 465113db2ddSJeff Roberson return; 466113db2ddSJeff Roberson } 467113db2ddSJeff Roberson } 468113db2ddSJeff Roberson ino_read(ino); 469113db2ddSJeff Roberson ino_dirty(ino); 470113db2ddSJeff Roberson } 471113db2ddSJeff Roberson 472113db2ddSJeff Roberson static void 473113db2ddSJeff Roberson iblk_write(struct ino_blk *iblk) 474113db2ddSJeff Roberson { 475113db2ddSJeff Roberson 476113db2ddSJeff Roberson if (iblk->ib_dirty == 0) 477113db2ddSJeff Roberson return; 478113db2ddSJeff Roberson if (bwrite(disk, fsbtodb(fs, iblk->ib_blk), iblk->ib_buf, 479113db2ddSJeff Roberson fs->fs_bsize) == -1) 480edad6026SXin LI err_suj("Failed to write inode block %jd\n", iblk->ib_blk); 481113db2ddSJeff Roberson } 482113db2ddSJeff Roberson 483113db2ddSJeff Roberson static int 484113db2ddSJeff Roberson blk_overlaps(struct jblkrec *brec, ufs2_daddr_t start, int frags) 485113db2ddSJeff Roberson { 486113db2ddSJeff Roberson ufs2_daddr_t bstart; 487113db2ddSJeff Roberson ufs2_daddr_t bend; 488113db2ddSJeff Roberson ufs2_daddr_t end; 489113db2ddSJeff Roberson 490113db2ddSJeff Roberson end = start + frags; 491113db2ddSJeff Roberson bstart = brec->jb_blkno + brec->jb_oldfrags; 492113db2ddSJeff Roberson bend = bstart + brec->jb_frags; 493113db2ddSJeff Roberson if (start < bend && end > bstart) 494113db2ddSJeff Roberson return (1); 495113db2ddSJeff Roberson return (0); 496113db2ddSJeff Roberson } 497113db2ddSJeff Roberson 498113db2ddSJeff Roberson static int 499113db2ddSJeff Roberson blk_equals(struct jblkrec *brec, ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t start, 500113db2ddSJeff Roberson int frags) 501113db2ddSJeff Roberson { 502113db2ddSJeff Roberson 503113db2ddSJeff Roberson if (brec->jb_ino != ino || brec->jb_lbn != lbn) 504113db2ddSJeff Roberson return (0); 505113db2ddSJeff Roberson if (brec->jb_blkno + brec->jb_oldfrags != start) 506113db2ddSJeff Roberson return (0); 5072db62a6bSJeff Roberson if (brec->jb_frags < frags) 508113db2ddSJeff Roberson return (0); 509113db2ddSJeff Roberson return (1); 510113db2ddSJeff Roberson } 511113db2ddSJeff Roberson 512113db2ddSJeff Roberson static void 513113db2ddSJeff Roberson blk_setmask(struct jblkrec *brec, int *mask) 514113db2ddSJeff Roberson { 515113db2ddSJeff Roberson int i; 516113db2ddSJeff Roberson 517113db2ddSJeff Roberson for (i = brec->jb_oldfrags; i < brec->jb_oldfrags + brec->jb_frags; i++) 518113db2ddSJeff Roberson *mask |= 1 << i; 519113db2ddSJeff Roberson } 520113db2ddSJeff Roberson 521113db2ddSJeff Roberson /* 522113db2ddSJeff Roberson * Determine whether a given block has been reallocated to a new location. 523113db2ddSJeff Roberson * Returns a mask of overlapping bits if any frags have been reused or 524113db2ddSJeff Roberson * zero if the block has not been re-used and the contents can be trusted. 525113db2ddSJeff Roberson * 526113db2ddSJeff Roberson * This is used to ensure that an orphaned pointer due to truncate is safe 527113db2ddSJeff Roberson * to be freed. The mask value can be used to free partial blocks. 528113db2ddSJeff Roberson */ 529113db2ddSJeff Roberson static int 530113db2ddSJeff Roberson blk_freemask(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags) 531113db2ddSJeff Roberson { 532113db2ddSJeff Roberson struct suj_blk *sblk; 533113db2ddSJeff Roberson struct suj_rec *srec; 534113db2ddSJeff Roberson struct jblkrec *brec; 535113db2ddSJeff Roberson int mask; 536113db2ddSJeff Roberson int off; 537113db2ddSJeff Roberson 538113db2ddSJeff Roberson /* 539113db2ddSJeff Roberson * To be certain we're not freeing a reallocated block we lookup 540113db2ddSJeff Roberson * this block in the blk hash and see if there is an allocation 541113db2ddSJeff Roberson * journal record that overlaps with any fragments in the block 542113db2ddSJeff Roberson * we're concerned with. If any fragments have ben reallocated 543113db2ddSJeff Roberson * the block has already been freed and re-used for another purpose. 544113db2ddSJeff Roberson */ 545113db2ddSJeff Roberson mask = 0; 546113db2ddSJeff Roberson sblk = blk_lookup(blknum(fs, blk), 0); 547113db2ddSJeff Roberson if (sblk == NULL) 548113db2ddSJeff Roberson return (0); 549113db2ddSJeff Roberson off = blk - sblk->sb_blk; 550113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 551113db2ddSJeff Roberson brec = (struct jblkrec *)srec->sr_rec; 552113db2ddSJeff Roberson /* 553113db2ddSJeff Roberson * If the block overlaps but does not match 554113db2ddSJeff Roberson * exactly this record refers to the current 555113db2ddSJeff Roberson * location. 556113db2ddSJeff Roberson */ 557113db2ddSJeff Roberson if (blk_overlaps(brec, blk, frags) == 0) 558113db2ddSJeff Roberson continue; 559113db2ddSJeff Roberson if (blk_equals(brec, ino, lbn, blk, frags) == 1) 560113db2ddSJeff Roberson mask = 0; 561113db2ddSJeff Roberson else 562113db2ddSJeff Roberson blk_setmask(brec, &mask); 563113db2ddSJeff Roberson } 564113db2ddSJeff Roberson if (debug) 565113db2ddSJeff Roberson printf("blk_freemask: blk %jd sblk %jd off %d mask 0x%X\n", 566113db2ddSJeff Roberson blk, sblk->sb_blk, off, mask); 567113db2ddSJeff Roberson return (mask >> off); 568113db2ddSJeff Roberson } 569113db2ddSJeff Roberson 570113db2ddSJeff Roberson /* 571113db2ddSJeff Roberson * Determine whether it is safe to follow an indirect. It is not safe 572113db2ddSJeff Roberson * if any part of the indirect has been reallocated or the last journal 573113db2ddSJeff Roberson * entry was an allocation. Just allocated indirects may not have valid 574113db2ddSJeff Roberson * pointers yet and all of their children will have their own records. 575113db2ddSJeff Roberson * It is also not safe to follow an indirect if the cg bitmap has been 576113db2ddSJeff Roberson * cleared as a new allocation may write to the block prior to the journal 577113db2ddSJeff Roberson * being written. 578113db2ddSJeff Roberson * 579113db2ddSJeff Roberson * Returns 1 if it's safe to follow the indirect and 0 otherwise. 580113db2ddSJeff Roberson */ 581113db2ddSJeff Roberson static int 582113db2ddSJeff Roberson blk_isindir(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn) 583113db2ddSJeff Roberson { 584113db2ddSJeff Roberson struct suj_blk *sblk; 585113db2ddSJeff Roberson struct jblkrec *brec; 586113db2ddSJeff Roberson 587113db2ddSJeff Roberson sblk = blk_lookup(blk, 0); 588113db2ddSJeff Roberson if (sblk == NULL) 589113db2ddSJeff Roberson return (1); 590113db2ddSJeff Roberson if (TAILQ_EMPTY(&sblk->sb_recs)) 591113db2ddSJeff Roberson return (1); 592113db2ddSJeff Roberson brec = (struct jblkrec *)TAILQ_LAST(&sblk->sb_recs, srechd)->sr_rec; 593113db2ddSJeff Roberson if (blk_equals(brec, ino, lbn, blk, fs->fs_frag)) 594113db2ddSJeff Roberson if (brec->jb_op == JOP_FREEBLK) 595113db2ddSJeff Roberson return (!blk_isfree(blk)); 596113db2ddSJeff Roberson return (0); 597113db2ddSJeff Roberson } 598113db2ddSJeff Roberson 599113db2ddSJeff Roberson /* 600113db2ddSJeff Roberson * Clear an inode from the cg bitmap. If the inode was already clear return 601113db2ddSJeff Roberson * 0 so the caller knows it does not have to check the inode contents. 602113db2ddSJeff Roberson */ 603113db2ddSJeff Roberson static int 604113db2ddSJeff Roberson ino_free(ino_t ino, int mode) 605113db2ddSJeff Roberson { 606113db2ddSJeff Roberson struct suj_cg *sc; 607113db2ddSJeff Roberson uint8_t *inosused; 608113db2ddSJeff Roberson struct cg *cgp; 609113db2ddSJeff Roberson int cg; 610113db2ddSJeff Roberson 611113db2ddSJeff Roberson cg = ino_to_cg(fs, ino); 612113db2ddSJeff Roberson ino = ino % fs->fs_ipg; 613113db2ddSJeff Roberson sc = cg_lookup(cg); 614113db2ddSJeff Roberson cgp = sc->sc_cgp; 615113db2ddSJeff Roberson inosused = cg_inosused(cgp); 616113db2ddSJeff Roberson /* 617113db2ddSJeff Roberson * The bitmap may never have made it to the disk so we have to 618113db2ddSJeff Roberson * conditionally clear. We can avoid writing the cg in this case. 619113db2ddSJeff Roberson */ 620113db2ddSJeff Roberson if (isclr(inosused, ino)) 621113db2ddSJeff Roberson return (0); 622113db2ddSJeff Roberson freeinos++; 623113db2ddSJeff Roberson clrbit(inosused, ino); 624113db2ddSJeff Roberson if (ino < cgp->cg_irotor) 625113db2ddSJeff Roberson cgp->cg_irotor = ino; 626113db2ddSJeff Roberson cgp->cg_cs.cs_nifree++; 627113db2ddSJeff Roberson if ((mode & IFMT) == IFDIR) { 628113db2ddSJeff Roberson freedir++; 629113db2ddSJeff Roberson cgp->cg_cs.cs_ndir--; 630113db2ddSJeff Roberson } 631113db2ddSJeff Roberson sc->sc_dirty = 1; 632113db2ddSJeff Roberson 633113db2ddSJeff Roberson return (1); 634113db2ddSJeff Roberson } 635113db2ddSJeff Roberson 636113db2ddSJeff Roberson /* 637113db2ddSJeff Roberson * Free 'frags' frags starting at filesystem block 'bno' skipping any frags 638113db2ddSJeff Roberson * set in the mask. 639113db2ddSJeff Roberson */ 640113db2ddSJeff Roberson static void 641113db2ddSJeff Roberson blk_free(ufs2_daddr_t bno, int mask, int frags) 642113db2ddSJeff Roberson { 643113db2ddSJeff Roberson ufs1_daddr_t fragno, cgbno; 644113db2ddSJeff Roberson struct suj_cg *sc; 645113db2ddSJeff Roberson struct cg *cgp; 646113db2ddSJeff Roberson int i, cg; 647113db2ddSJeff Roberson uint8_t *blksfree; 648113db2ddSJeff Roberson 649113db2ddSJeff Roberson if (debug) 6502db62a6bSJeff Roberson printf("Freeing %d frags at blk %jd mask 0x%x\n", 6512db62a6bSJeff Roberson frags, bno, mask); 652113db2ddSJeff Roberson cg = dtog(fs, bno); 653113db2ddSJeff Roberson sc = cg_lookup(cg); 654113db2ddSJeff Roberson cgp = sc->sc_cgp; 655113db2ddSJeff Roberson cgbno = dtogd(fs, bno); 656113db2ddSJeff Roberson blksfree = cg_blksfree(cgp); 657113db2ddSJeff Roberson 658113db2ddSJeff Roberson /* 659113db2ddSJeff Roberson * If it's not allocated we only wrote the journal entry 660113db2ddSJeff Roberson * and never the bitmaps. Here we unconditionally clear and 661113db2ddSJeff Roberson * resolve the cg summary later. 662113db2ddSJeff Roberson */ 663113db2ddSJeff Roberson if (frags == fs->fs_frag && mask == 0) { 664113db2ddSJeff Roberson fragno = fragstoblks(fs, cgbno); 665113db2ddSJeff Roberson ffs_setblock(fs, blksfree, fragno); 666113db2ddSJeff Roberson freeblocks++; 667113db2ddSJeff Roberson } else { 668113db2ddSJeff Roberson /* 669113db2ddSJeff Roberson * deallocate the fragment 670113db2ddSJeff Roberson */ 671113db2ddSJeff Roberson for (i = 0; i < frags; i++) 672113db2ddSJeff Roberson if ((mask & (1 << i)) == 0 && isclr(blksfree, cgbno +i)) { 673113db2ddSJeff Roberson freefrags++; 674113db2ddSJeff Roberson setbit(blksfree, cgbno + i); 675113db2ddSJeff Roberson } 676113db2ddSJeff Roberson } 677113db2ddSJeff Roberson sc->sc_dirty = 1; 678113db2ddSJeff Roberson } 679113db2ddSJeff Roberson 680113db2ddSJeff Roberson /* 681113db2ddSJeff Roberson * Returns 1 if the whole block starting at 'bno' is marked free and 0 682113db2ddSJeff Roberson * otherwise. 683113db2ddSJeff Roberson */ 684113db2ddSJeff Roberson static int 685113db2ddSJeff Roberson blk_isfree(ufs2_daddr_t bno) 686113db2ddSJeff Roberson { 687113db2ddSJeff Roberson struct suj_cg *sc; 688113db2ddSJeff Roberson 689113db2ddSJeff Roberson sc = cg_lookup(dtog(fs, bno)); 690113db2ddSJeff Roberson return ffs_isblock(fs, cg_blksfree(sc->sc_cgp), dtogd(fs, bno)); 691113db2ddSJeff Roberson } 692113db2ddSJeff Roberson 693113db2ddSJeff Roberson /* 694113db2ddSJeff Roberson * Fetch an indirect block to find the block at a given lbn. The lbn 695113db2ddSJeff Roberson * may be negative to fetch a specific indirect block pointer or positive 696113db2ddSJeff Roberson * to fetch a specific block. 697113db2ddSJeff Roberson */ 698113db2ddSJeff Roberson static ufs2_daddr_t 699113db2ddSJeff Roberson indir_blkatoff(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t cur, ufs_lbn_t lbn) 700113db2ddSJeff Roberson { 701113db2ddSJeff Roberson ufs2_daddr_t *bap2; 702113db2ddSJeff Roberson ufs2_daddr_t *bap1; 703113db2ddSJeff Roberson ufs_lbn_t lbnadd; 704113db2ddSJeff Roberson ufs_lbn_t base; 705113db2ddSJeff Roberson int level; 706113db2ddSJeff Roberson int i; 707113db2ddSJeff Roberson 708113db2ddSJeff Roberson if (blk == 0) 709113db2ddSJeff Roberson return (0); 710113db2ddSJeff Roberson level = lbn_level(cur); 711113db2ddSJeff Roberson if (level == -1) 712edad6026SXin LI err_suj("Invalid indir lbn %jd\n", lbn); 713113db2ddSJeff Roberson if (level == 0 && lbn < 0) 714edad6026SXin LI err_suj("Invalid lbn %jd\n", lbn); 715113db2ddSJeff Roberson bap2 = (void *)dblk_read(blk, fs->fs_bsize); 716113db2ddSJeff Roberson bap1 = (void *)bap2; 717113db2ddSJeff Roberson lbnadd = 1; 718113db2ddSJeff Roberson base = -(cur + level); 719113db2ddSJeff Roberson for (i = level; i > 0; i--) 720113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 721113db2ddSJeff Roberson if (lbn > 0) 722113db2ddSJeff Roberson i = (lbn - base) / lbnadd; 723113db2ddSJeff Roberson else 724113db2ddSJeff Roberson i = (-lbn - base) / lbnadd; 725113db2ddSJeff Roberson if (i < 0 || i >= NINDIR(fs)) 726edad6026SXin LI err_suj("Invalid indirect index %d produced by lbn %jd\n", 727113db2ddSJeff Roberson i, lbn); 728113db2ddSJeff Roberson if (level == 0) 729113db2ddSJeff Roberson cur = base + (i * lbnadd); 730113db2ddSJeff Roberson else 731113db2ddSJeff Roberson cur = -(base + (i * lbnadd)) - (level - 1); 732113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 733113db2ddSJeff Roberson blk = bap1[i]; 734113db2ddSJeff Roberson else 735113db2ddSJeff Roberson blk = bap2[i]; 736113db2ddSJeff Roberson if (cur == lbn) 737113db2ddSJeff Roberson return (blk); 738edad6026SXin LI if (level == 0) 739edad6026SXin LI err_suj("Invalid lbn %jd at level 0\n", lbn); 740113db2ddSJeff Roberson return indir_blkatoff(blk, ino, cur, lbn); 741113db2ddSJeff Roberson } 742113db2ddSJeff Roberson 743113db2ddSJeff Roberson /* 744113db2ddSJeff Roberson * Finds the disk block address at the specified lbn within the inode 745113db2ddSJeff Roberson * specified by ip. This follows the whole tree and honors di_size and 746113db2ddSJeff Roberson * di_extsize so it is a true test of reachability. The lbn may be 747113db2ddSJeff Roberson * negative if an extattr or indirect block is requested. 748113db2ddSJeff Roberson */ 749113db2ddSJeff Roberson static ufs2_daddr_t 750113db2ddSJeff Roberson ino_blkatoff(union dinode *ip, ino_t ino, ufs_lbn_t lbn, int *frags) 751113db2ddSJeff Roberson { 752113db2ddSJeff Roberson ufs_lbn_t tmpval; 753113db2ddSJeff Roberson ufs_lbn_t cur; 754113db2ddSJeff Roberson ufs_lbn_t next; 755113db2ddSJeff Roberson int i; 756113db2ddSJeff Roberson 757113db2ddSJeff Roberson /* 758113db2ddSJeff Roberson * Handle extattr blocks first. 759113db2ddSJeff Roberson */ 760113db2ddSJeff Roberson if (lbn < 0 && lbn >= -NXADDR) { 761113db2ddSJeff Roberson lbn = -1 - lbn; 762113db2ddSJeff Roberson if (lbn > lblkno(fs, ip->dp2.di_extsize - 1)) 763113db2ddSJeff Roberson return (0); 764113db2ddSJeff Roberson *frags = numfrags(fs, sblksize(fs, ip->dp2.di_extsize, lbn)); 765113db2ddSJeff Roberson return (ip->dp2.di_extb[lbn]); 766113db2ddSJeff Roberson } 767113db2ddSJeff Roberson /* 768113db2ddSJeff Roberson * Now direct and indirect. 769113db2ddSJeff Roberson */ 770113db2ddSJeff Roberson if (DIP(ip, di_mode) == IFLNK && 771113db2ddSJeff Roberson DIP(ip, di_size) < fs->fs_maxsymlinklen) 772113db2ddSJeff Roberson return (0); 773113db2ddSJeff Roberson if (lbn >= 0 && lbn < NDADDR) { 774113db2ddSJeff Roberson *frags = numfrags(fs, sblksize(fs, DIP(ip, di_size), lbn)); 775113db2ddSJeff Roberson return (DIP(ip, di_db[lbn])); 776113db2ddSJeff Roberson } 777113db2ddSJeff Roberson *frags = fs->fs_frag; 778113db2ddSJeff Roberson 779113db2ddSJeff Roberson for (i = 0, tmpval = NINDIR(fs), cur = NDADDR; i < NIADDR; i++, 780113db2ddSJeff Roberson tmpval *= NINDIR(fs), cur = next) { 781113db2ddSJeff Roberson next = cur + tmpval; 782113db2ddSJeff Roberson if (lbn == -cur - i) 783113db2ddSJeff Roberson return (DIP(ip, di_ib[i])); 784113db2ddSJeff Roberson /* 785113db2ddSJeff Roberson * Determine whether the lbn in question is within this tree. 786113db2ddSJeff Roberson */ 787113db2ddSJeff Roberson if (lbn < 0 && -lbn >= next) 788113db2ddSJeff Roberson continue; 789113db2ddSJeff Roberson if (lbn > 0 && lbn >= next) 790113db2ddSJeff Roberson continue; 791113db2ddSJeff Roberson return indir_blkatoff(DIP(ip, di_ib[i]), ino, -cur - i, lbn); 792113db2ddSJeff Roberson } 793edad6026SXin LI err_suj("lbn %jd not in ino\n", lbn); 794edad6026SXin LI /* NOTREACHED */ 795113db2ddSJeff Roberson } 796113db2ddSJeff Roberson 797113db2ddSJeff Roberson /* 798113db2ddSJeff Roberson * Determine whether a block exists at a particular lbn in an inode. 799113db2ddSJeff Roberson * Returns 1 if found, 0 if not. lbn may be negative for indirects 800113db2ddSJeff Roberson * or ext blocks. 801113db2ddSJeff Roberson */ 802113db2ddSJeff Roberson static int 803113db2ddSJeff Roberson blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int *frags) 804113db2ddSJeff Roberson { 805113db2ddSJeff Roberson union dinode *ip; 806113db2ddSJeff Roberson ufs2_daddr_t nblk; 807113db2ddSJeff Roberson 808113db2ddSJeff Roberson ip = ino_read(ino); 809113db2ddSJeff Roberson 810113db2ddSJeff Roberson if (DIP(ip, di_nlink) == 0 || DIP(ip, di_mode) == 0) 811113db2ddSJeff Roberson return (0); 812113db2ddSJeff Roberson nblk = ino_blkatoff(ip, ino, lbn, frags); 813113db2ddSJeff Roberson 814113db2ddSJeff Roberson return (nblk == blk); 815113db2ddSJeff Roberson } 816113db2ddSJeff Roberson 817113db2ddSJeff Roberson /* 81824d37c1eSJeff Roberson * Clear the directory entry at diroff that should point to child. Minimal 81924d37c1eSJeff Roberson * checking is done and it is assumed that this path was verified with isat. 82024d37c1eSJeff Roberson */ 82124d37c1eSJeff Roberson static void 82224d37c1eSJeff Roberson ino_clrat(ino_t parent, off_t diroff, ino_t child) 82324d37c1eSJeff Roberson { 82424d37c1eSJeff Roberson union dinode *dip; 82524d37c1eSJeff Roberson struct direct *dp; 82624d37c1eSJeff Roberson ufs2_daddr_t blk; 82724d37c1eSJeff Roberson uint8_t *block; 82824d37c1eSJeff Roberson ufs_lbn_t lbn; 82924d37c1eSJeff Roberson int blksize; 83024d37c1eSJeff Roberson int frags; 83124d37c1eSJeff Roberson int doff; 83224d37c1eSJeff Roberson 83324d37c1eSJeff Roberson if (debug) 834623d7cb6SMatthew D Fleming printf("Clearing inode %ju from parent %ju at offset %jd\n", 835623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff); 83624d37c1eSJeff Roberson 83724d37c1eSJeff Roberson lbn = lblkno(fs, diroff); 83824d37c1eSJeff Roberson doff = blkoff(fs, diroff); 83924d37c1eSJeff Roberson dip = ino_read(parent); 84024d37c1eSJeff Roberson blk = ino_blkatoff(dip, parent, lbn, &frags); 84124d37c1eSJeff Roberson blksize = sblksize(fs, DIP(dip, di_size), lbn); 84224d37c1eSJeff Roberson block = dblk_read(blk, blksize); 84324d37c1eSJeff Roberson dp = (struct direct *)&block[doff]; 84424d37c1eSJeff Roberson if (dp->d_ino != child) 845623d7cb6SMatthew D Fleming errx(1, "Inode %ju does not exist in %ju at %jd", 846623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff); 84724d37c1eSJeff Roberson dp->d_ino = 0; 84824d37c1eSJeff Roberson dblk_dirty(blk); 84924d37c1eSJeff Roberson /* 85024d37c1eSJeff Roberson * The actual .. reference count will already have been removed 85124d37c1eSJeff Roberson * from the parent by the .. remref record. 85224d37c1eSJeff Roberson */ 85324d37c1eSJeff Roberson } 85424d37c1eSJeff Roberson 85524d37c1eSJeff Roberson /* 856113db2ddSJeff Roberson * Determines whether a pointer to an inode exists within a directory 857113db2ddSJeff Roberson * at a specified offset. Returns the mode of the found entry. 858113db2ddSJeff Roberson */ 859113db2ddSJeff Roberson static int 860113db2ddSJeff Roberson ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot) 861113db2ddSJeff Roberson { 862113db2ddSJeff Roberson union dinode *dip; 863113db2ddSJeff Roberson struct direct *dp; 864113db2ddSJeff Roberson ufs2_daddr_t blk; 865113db2ddSJeff Roberson uint8_t *block; 866113db2ddSJeff Roberson ufs_lbn_t lbn; 867113db2ddSJeff Roberson int blksize; 868113db2ddSJeff Roberson int frags; 869113db2ddSJeff Roberson int dpoff; 870113db2ddSJeff Roberson int doff; 871113db2ddSJeff Roberson 872113db2ddSJeff Roberson *isdot = 0; 873113db2ddSJeff Roberson dip = ino_read(parent); 874113db2ddSJeff Roberson *mode = DIP(dip, di_mode); 875113db2ddSJeff Roberson if ((*mode & IFMT) != IFDIR) { 876113db2ddSJeff Roberson if (debug) { 877113db2ddSJeff Roberson /* 878113db2ddSJeff Roberson * This can happen if the parent inode 879113db2ddSJeff Roberson * was reallocated. 880113db2ddSJeff Roberson */ 881113db2ddSJeff Roberson if (*mode != 0) 882623d7cb6SMatthew D Fleming printf("Directory %ju has bad mode %o\n", 883623d7cb6SMatthew D Fleming (uintmax_t)parent, *mode); 884113db2ddSJeff Roberson else 885623d7cb6SMatthew D Fleming printf("Directory %ju has zero mode\n", 886623d7cb6SMatthew D Fleming (uintmax_t)parent); 887113db2ddSJeff Roberson } 888113db2ddSJeff Roberson return (0); 889113db2ddSJeff Roberson } 890113db2ddSJeff Roberson lbn = lblkno(fs, diroff); 891113db2ddSJeff Roberson doff = blkoff(fs, diroff); 892113db2ddSJeff Roberson blksize = sblksize(fs, DIP(dip, di_size), lbn); 893113db2ddSJeff Roberson if (diroff + DIRECTSIZ(1) > DIP(dip, di_size) || doff >= blksize) { 894113db2ddSJeff Roberson if (debug) 895623d7cb6SMatthew D Fleming printf("ino %ju absent from %ju due to offset %jd" 896113db2ddSJeff Roberson " exceeding size %jd\n", 897623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff, 898623d7cb6SMatthew D Fleming DIP(dip, di_size)); 899113db2ddSJeff Roberson return (0); 900113db2ddSJeff Roberson } 901113db2ddSJeff Roberson blk = ino_blkatoff(dip, parent, lbn, &frags); 902113db2ddSJeff Roberson if (blk <= 0) { 903113db2ddSJeff Roberson if (debug) 904623d7cb6SMatthew D Fleming printf("Sparse directory %ju", (uintmax_t)parent); 905113db2ddSJeff Roberson return (0); 906113db2ddSJeff Roberson } 907113db2ddSJeff Roberson block = dblk_read(blk, blksize); 908113db2ddSJeff Roberson /* 909113db2ddSJeff Roberson * Walk through the records from the start of the block to be 910113db2ddSJeff Roberson * certain we hit a valid record and not some junk in the middle 911113db2ddSJeff Roberson * of a file name. Stop when we reach or pass the expected offset. 912113db2ddSJeff Roberson */ 913113db2ddSJeff Roberson dpoff = (doff / DIRBLKSIZ) * DIRBLKSIZ; 914113db2ddSJeff Roberson do { 915113db2ddSJeff Roberson dp = (struct direct *)&block[dpoff]; 916113db2ddSJeff Roberson if (dpoff == doff) 917113db2ddSJeff Roberson break; 918113db2ddSJeff Roberson if (dp->d_reclen == 0) 919113db2ddSJeff Roberson break; 920113db2ddSJeff Roberson dpoff += dp->d_reclen; 921113db2ddSJeff Roberson } while (dpoff <= doff); 922113db2ddSJeff Roberson if (dpoff > fs->fs_bsize) 923623d7cb6SMatthew D Fleming err_suj("Corrupt directory block in dir ino %ju\n", 924623d7cb6SMatthew D Fleming (uintmax_t)parent); 925113db2ddSJeff Roberson /* Not found. */ 926113db2ddSJeff Roberson if (dpoff != doff) { 927113db2ddSJeff Roberson if (debug) 928623d7cb6SMatthew D Fleming printf("ino %ju not found in %ju, lbn %jd, dpoff %d\n", 929623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, lbn, dpoff); 930113db2ddSJeff Roberson return (0); 931113db2ddSJeff Roberson } 932113db2ddSJeff Roberson /* 933113db2ddSJeff Roberson * We found the item in question. Record the mode and whether it's 934113db2ddSJeff Roberson * a . or .. link for the caller. 935113db2ddSJeff Roberson */ 936113db2ddSJeff Roberson if (dp->d_ino == child) { 937113db2ddSJeff Roberson if (child == parent) 938113db2ddSJeff Roberson *isdot = 1; 939113db2ddSJeff Roberson else if (dp->d_namlen == 2 && 940113db2ddSJeff Roberson dp->d_name[0] == '.' && dp->d_name[1] == '.') 941113db2ddSJeff Roberson *isdot = 1; 942113db2ddSJeff Roberson *mode = DTTOIF(dp->d_type); 943113db2ddSJeff Roberson return (1); 944113db2ddSJeff Roberson } 945113db2ddSJeff Roberson if (debug) 946623d7cb6SMatthew D Fleming printf("ino %ju doesn't match dirent ino %ju in parent %ju\n", 947623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)dp->d_ino, (uintmax_t)parent); 948113db2ddSJeff Roberson return (0); 949113db2ddSJeff Roberson } 950113db2ddSJeff Roberson 951113db2ddSJeff Roberson #define VISIT_INDIR 0x0001 952113db2ddSJeff Roberson #define VISIT_EXT 0x0002 953113db2ddSJeff Roberson #define VISIT_ROOT 0x0004 /* Operation came via root & valid pointers. */ 954113db2ddSJeff Roberson 955113db2ddSJeff Roberson /* 956113db2ddSJeff Roberson * Read an indirect level which may or may not be linked into an inode. 957113db2ddSJeff Roberson */ 958113db2ddSJeff Roberson static void 959113db2ddSJeff Roberson indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags, 960113db2ddSJeff Roberson ino_visitor visitor, int flags) 961113db2ddSJeff Roberson { 962113db2ddSJeff Roberson ufs2_daddr_t *bap2; 963113db2ddSJeff Roberson ufs1_daddr_t *bap1; 964113db2ddSJeff Roberson ufs_lbn_t lbnadd; 965113db2ddSJeff Roberson ufs2_daddr_t nblk; 966113db2ddSJeff Roberson ufs_lbn_t nlbn; 967113db2ddSJeff Roberson int level; 968113db2ddSJeff Roberson int i; 969113db2ddSJeff Roberson 970113db2ddSJeff Roberson /* 971113db2ddSJeff Roberson * Don't visit indirect blocks with contents we can't trust. This 972113db2ddSJeff Roberson * should only happen when indir_visit() is called to complete a 973113db2ddSJeff Roberson * truncate that never finished and not when a pointer is found via 974113db2ddSJeff Roberson * an inode. 975113db2ddSJeff Roberson */ 976113db2ddSJeff Roberson if (blk == 0) 977113db2ddSJeff Roberson return; 978113db2ddSJeff Roberson level = lbn_level(lbn); 979113db2ddSJeff Roberson if (level == -1) 980edad6026SXin LI err_suj("Invalid level for lbn %jd\n", lbn); 981113db2ddSJeff Roberson if ((flags & VISIT_ROOT) == 0 && blk_isindir(blk, ino, lbn) == 0) { 982113db2ddSJeff Roberson if (debug) 983623d7cb6SMatthew D Fleming printf("blk %jd ino %ju lbn %jd(%d) is not indir.\n", 984623d7cb6SMatthew D Fleming blk, (uintmax_t)ino, lbn, level); 985113db2ddSJeff Roberson goto out; 986113db2ddSJeff Roberson } 987113db2ddSJeff Roberson lbnadd = 1; 988113db2ddSJeff Roberson for (i = level; i > 0; i--) 989113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 990113db2ddSJeff Roberson bap1 = (void *)dblk_read(blk, fs->fs_bsize); 991113db2ddSJeff Roberson bap2 = (void *)bap1; 992113db2ddSJeff Roberson for (i = 0; i < NINDIR(fs); i++) { 993113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 994113db2ddSJeff Roberson nblk = *bap1++; 995113db2ddSJeff Roberson else 996113db2ddSJeff Roberson nblk = *bap2++; 997113db2ddSJeff Roberson if (nblk == 0) 998113db2ddSJeff Roberson continue; 999113db2ddSJeff Roberson if (level == 0) { 1000113db2ddSJeff Roberson nlbn = -lbn + i * lbnadd; 1001113db2ddSJeff Roberson (*frags) += fs->fs_frag; 1002113db2ddSJeff Roberson visitor(ino, nlbn, nblk, fs->fs_frag); 1003113db2ddSJeff Roberson } else { 1004113db2ddSJeff Roberson nlbn = (lbn + 1) - (i * lbnadd); 1005113db2ddSJeff Roberson indir_visit(ino, nlbn, nblk, frags, visitor, flags); 1006113db2ddSJeff Roberson } 1007113db2ddSJeff Roberson } 1008113db2ddSJeff Roberson out: 1009113db2ddSJeff Roberson if (flags & VISIT_INDIR) { 1010113db2ddSJeff Roberson (*frags) += fs->fs_frag; 1011113db2ddSJeff Roberson visitor(ino, lbn, blk, fs->fs_frag); 1012113db2ddSJeff Roberson } 1013113db2ddSJeff Roberson } 1014113db2ddSJeff Roberson 1015113db2ddSJeff Roberson /* 1016113db2ddSJeff Roberson * Visit each block in an inode as specified by 'flags' and call a 1017113db2ddSJeff Roberson * callback function. The callback may inspect or free blocks. The 1018113db2ddSJeff Roberson * count of frags found according to the size in the file is returned. 1019113db2ddSJeff Roberson * This is not valid for sparse files but may be used to determine 1020113db2ddSJeff Roberson * the correct di_blocks for a file. 1021113db2ddSJeff Roberson */ 1022113db2ddSJeff Roberson static uint64_t 1023113db2ddSJeff Roberson ino_visit(union dinode *ip, ino_t ino, ino_visitor visitor, int flags) 1024113db2ddSJeff Roberson { 1025113db2ddSJeff Roberson ufs_lbn_t nextlbn; 1026113db2ddSJeff Roberson ufs_lbn_t tmpval; 1027113db2ddSJeff Roberson ufs_lbn_t lbn; 1028113db2ddSJeff Roberson uint64_t size; 1029113db2ddSJeff Roberson uint64_t fragcnt; 1030113db2ddSJeff Roberson int mode; 1031113db2ddSJeff Roberson int frags; 1032113db2ddSJeff Roberson int i; 1033113db2ddSJeff Roberson 1034113db2ddSJeff Roberson size = DIP(ip, di_size); 1035113db2ddSJeff Roberson mode = DIP(ip, di_mode) & IFMT; 1036113db2ddSJeff Roberson fragcnt = 0; 1037113db2ddSJeff Roberson if ((flags & VISIT_EXT) && 1038113db2ddSJeff Roberson fs->fs_magic == FS_UFS2_MAGIC && ip->dp2.di_extsize) { 1039113db2ddSJeff Roberson for (i = 0; i < NXADDR; i++) { 1040113db2ddSJeff Roberson if (ip->dp2.di_extb[i] == 0) 1041113db2ddSJeff Roberson continue; 1042113db2ddSJeff Roberson frags = sblksize(fs, ip->dp2.di_extsize, i); 1043113db2ddSJeff Roberson frags = numfrags(fs, frags); 1044113db2ddSJeff Roberson fragcnt += frags; 1045113db2ddSJeff Roberson visitor(ino, -1 - i, ip->dp2.di_extb[i], frags); 1046113db2ddSJeff Roberson } 1047113db2ddSJeff Roberson } 1048113db2ddSJeff Roberson /* Skip datablocks for short links and devices. */ 1049113db2ddSJeff Roberson if (mode == IFBLK || mode == IFCHR || 1050113db2ddSJeff Roberson (mode == IFLNK && size < fs->fs_maxsymlinklen)) 1051113db2ddSJeff Roberson return (fragcnt); 1052113db2ddSJeff Roberson for (i = 0; i < NDADDR; i++) { 1053113db2ddSJeff Roberson if (DIP(ip, di_db[i]) == 0) 1054113db2ddSJeff Roberson continue; 1055113db2ddSJeff Roberson frags = sblksize(fs, size, i); 1056113db2ddSJeff Roberson frags = numfrags(fs, frags); 1057113db2ddSJeff Roberson fragcnt += frags; 1058113db2ddSJeff Roberson visitor(ino, i, DIP(ip, di_db[i]), frags); 1059113db2ddSJeff Roberson } 1060113db2ddSJeff Roberson /* 1061113db2ddSJeff Roberson * We know the following indirects are real as we're following 1062113db2ddSJeff Roberson * real pointers to them. 1063113db2ddSJeff Roberson */ 1064113db2ddSJeff Roberson flags |= VISIT_ROOT; 1065113db2ddSJeff Roberson for (i = 0, tmpval = NINDIR(fs), lbn = NDADDR; i < NIADDR; i++, 1066113db2ddSJeff Roberson lbn = nextlbn) { 1067113db2ddSJeff Roberson nextlbn = lbn + tmpval; 1068113db2ddSJeff Roberson tmpval *= NINDIR(fs); 1069113db2ddSJeff Roberson if (DIP(ip, di_ib[i]) == 0) 1070113db2ddSJeff Roberson continue; 1071113db2ddSJeff Roberson indir_visit(ino, -lbn - i, DIP(ip, di_ib[i]), &fragcnt, visitor, 1072113db2ddSJeff Roberson flags); 1073113db2ddSJeff Roberson } 1074113db2ddSJeff Roberson return (fragcnt); 1075113db2ddSJeff Roberson } 1076113db2ddSJeff Roberson 1077113db2ddSJeff Roberson /* 1078113db2ddSJeff Roberson * Null visitor function used when we just want to count blocks and 1079113db2ddSJeff Roberson * record the lbn. 1080113db2ddSJeff Roberson */ 1081113db2ddSJeff Roberson ufs_lbn_t visitlbn; 1082113db2ddSJeff Roberson static void 1083113db2ddSJeff Roberson null_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 1084113db2ddSJeff Roberson { 1085113db2ddSJeff Roberson if (lbn > 0) 1086113db2ddSJeff Roberson visitlbn = lbn; 1087113db2ddSJeff Roberson } 1088113db2ddSJeff Roberson 1089113db2ddSJeff Roberson /* 1090113db2ddSJeff Roberson * Recalculate di_blocks when we discover that a block allocation or 1091113db2ddSJeff Roberson * free was not successfully completed. The kernel does not roll this back 1092113db2ddSJeff Roberson * because it would be too expensive to compute which indirects were 1093113db2ddSJeff Roberson * reachable at the time the inode was written. 1094113db2ddSJeff Roberson */ 1095113db2ddSJeff Roberson static void 1096113db2ddSJeff Roberson ino_adjblks(struct suj_ino *sino) 1097113db2ddSJeff Roberson { 1098113db2ddSJeff Roberson union dinode *ip; 1099113db2ddSJeff Roberson uint64_t blocks; 1100113db2ddSJeff Roberson uint64_t frags; 1101113db2ddSJeff Roberson off_t isize; 1102113db2ddSJeff Roberson off_t size; 1103113db2ddSJeff Roberson ino_t ino; 1104113db2ddSJeff Roberson 1105113db2ddSJeff Roberson ino = sino->si_ino; 1106113db2ddSJeff Roberson ip = ino_read(ino); 1107113db2ddSJeff Roberson /* No need to adjust zero'd inodes. */ 1108113db2ddSJeff Roberson if (DIP(ip, di_mode) == 0) 1109113db2ddSJeff Roberson return; 1110113db2ddSJeff Roberson /* 1111113db2ddSJeff Roberson * Visit all blocks and count them as well as recording the last 1112113db2ddSJeff Roberson * valid lbn in the file. If the file size doesn't agree with the 1113113db2ddSJeff Roberson * last lbn we need to truncate to fix it. Otherwise just adjust 1114113db2ddSJeff Roberson * the blocks count. 1115113db2ddSJeff Roberson */ 1116113db2ddSJeff Roberson visitlbn = 0; 1117113db2ddSJeff Roberson frags = ino_visit(ip, ino, null_visit, VISIT_INDIR | VISIT_EXT); 1118113db2ddSJeff Roberson blocks = fsbtodb(fs, frags); 1119113db2ddSJeff Roberson /* 1120113db2ddSJeff Roberson * We assume the size and direct block list is kept coherent by 1121113db2ddSJeff Roberson * softdep. For files that have extended into indirects we truncate 1122113db2ddSJeff Roberson * to the size in the inode or the maximum size permitted by 1123113db2ddSJeff Roberson * populated indirects. 1124113db2ddSJeff Roberson */ 1125113db2ddSJeff Roberson if (visitlbn >= NDADDR) { 1126113db2ddSJeff Roberson isize = DIP(ip, di_size); 1127113db2ddSJeff Roberson size = lblktosize(fs, visitlbn + 1); 1128113db2ddSJeff Roberson if (isize > size) 1129113db2ddSJeff Roberson isize = size; 1130113db2ddSJeff Roberson /* Always truncate to free any unpopulated indirects. */ 1131113db2ddSJeff Roberson ino_trunc(sino->si_ino, isize); 1132113db2ddSJeff Roberson return; 1133113db2ddSJeff Roberson } 1134113db2ddSJeff Roberson if (blocks == DIP(ip, di_blocks)) 1135113db2ddSJeff Roberson return; 1136113db2ddSJeff Roberson if (debug) 1137623d7cb6SMatthew D Fleming printf("ino %ju adjusting block count from %jd to %jd\n", 1138623d7cb6SMatthew D Fleming (uintmax_t)ino, DIP(ip, di_blocks), blocks); 1139113db2ddSJeff Roberson DIP_SET(ip, di_blocks, blocks); 1140113db2ddSJeff Roberson ino_dirty(ino); 1141113db2ddSJeff Roberson } 1142113db2ddSJeff Roberson 1143113db2ddSJeff Roberson static void 1144113db2ddSJeff Roberson blk_free_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 1145113db2ddSJeff Roberson { 1146113db2ddSJeff Roberson 11472db62a6bSJeff Roberson blk_free(blk, blk_freemask(blk, ino, lbn, frags), frags); 1148113db2ddSJeff Roberson } 1149113db2ddSJeff Roberson 1150113db2ddSJeff Roberson /* 1151113db2ddSJeff Roberson * Free a block or tree of blocks that was previously rooted in ino at 1152113db2ddSJeff Roberson * the given lbn. If the lbn is an indirect all children are freed 1153113db2ddSJeff Roberson * recursively. 1154113db2ddSJeff Roberson */ 1155113db2ddSJeff Roberson static void 1156113db2ddSJeff Roberson blk_free_lbn(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags, int follow) 1157113db2ddSJeff Roberson { 1158113db2ddSJeff Roberson uint64_t resid; 1159113db2ddSJeff Roberson int mask; 1160113db2ddSJeff Roberson 1161113db2ddSJeff Roberson mask = blk_freemask(blk, ino, lbn, frags); 1162113db2ddSJeff Roberson resid = 0; 1163113db2ddSJeff Roberson if (lbn <= -NDADDR && follow && mask == 0) 1164113db2ddSJeff Roberson indir_visit(ino, lbn, blk, &resid, blk_free_visit, VISIT_INDIR); 1165113db2ddSJeff Roberson else 1166113db2ddSJeff Roberson blk_free(blk, mask, frags); 1167113db2ddSJeff Roberson } 1168113db2ddSJeff Roberson 1169113db2ddSJeff Roberson static void 1170113db2ddSJeff Roberson ino_setskip(struct suj_ino *sino, ino_t parent) 1171113db2ddSJeff Roberson { 1172113db2ddSJeff Roberson int isdot; 1173113db2ddSJeff Roberson int mode; 1174113db2ddSJeff Roberson 1175113db2ddSJeff Roberson if (ino_isat(sino->si_ino, DOTDOT_OFFSET, parent, &mode, &isdot)) 1176113db2ddSJeff Roberson sino->si_skipparent = 1; 1177113db2ddSJeff Roberson } 1178113db2ddSJeff Roberson 117924d37c1eSJeff Roberson static void 118024d37c1eSJeff Roberson ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot) 118124d37c1eSJeff Roberson { 118224d37c1eSJeff Roberson struct suj_ino *sino; 118324d37c1eSJeff Roberson struct suj_rec *srec; 118424d37c1eSJeff Roberson struct jrefrec *rrec; 118524d37c1eSJeff Roberson 118624d37c1eSJeff Roberson /* 118724d37c1eSJeff Roberson * Lookup this inode to see if we have a record for it. 118824d37c1eSJeff Roberson */ 118924d37c1eSJeff Roberson sino = ino_lookup(child, 0); 119024d37c1eSJeff Roberson /* 119124d37c1eSJeff Roberson * Tell any child directories we've already removed their 119224d37c1eSJeff Roberson * parent link cnt. Don't try to adjust our link down again. 119324d37c1eSJeff Roberson */ 119424d37c1eSJeff Roberson if (sino != NULL && isdotdot == 0) 119524d37c1eSJeff Roberson ino_setskip(sino, parent); 119624d37c1eSJeff Roberson /* 119724d37c1eSJeff Roberson * No valid record for this inode. Just drop the on-disk 119824d37c1eSJeff Roberson * link by one. 119924d37c1eSJeff Roberson */ 120024d37c1eSJeff Roberson if (sino == NULL || sino->si_hasrecs == 0) { 120124d37c1eSJeff Roberson ino_decr(child); 120224d37c1eSJeff Roberson return; 120324d37c1eSJeff Roberson } 120424d37c1eSJeff Roberson /* 120524d37c1eSJeff Roberson * Use ino_adjust() if ino_check() has already processed this 120624d37c1eSJeff Roberson * child. If we lose the last non-dot reference to a 120724d37c1eSJeff Roberson * directory it will be discarded. 120824d37c1eSJeff Roberson */ 120924d37c1eSJeff Roberson if (sino->si_linkadj) { 121024d37c1eSJeff Roberson sino->si_nlink--; 121124d37c1eSJeff Roberson if (isdotdot) 121224d37c1eSJeff Roberson sino->si_dotlinks--; 121324d37c1eSJeff Roberson ino_adjust(sino); 121424d37c1eSJeff Roberson return; 121524d37c1eSJeff Roberson } 121624d37c1eSJeff Roberson /* 121724d37c1eSJeff Roberson * If we haven't yet processed this inode we need to make 121824d37c1eSJeff Roberson * sure we will successfully discover the lost path. If not 121924d37c1eSJeff Roberson * use nlinkadj to remember. 122024d37c1eSJeff Roberson */ 122124d37c1eSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 122224d37c1eSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 122324d37c1eSJeff Roberson if (rrec->jr_parent == parent && 122424d37c1eSJeff Roberson rrec->jr_diroff == diroff) 122524d37c1eSJeff Roberson return; 122624d37c1eSJeff Roberson } 122724d37c1eSJeff Roberson sino->si_nlinkadj++; 122824d37c1eSJeff Roberson } 122924d37c1eSJeff Roberson 1230113db2ddSJeff Roberson /* 1231113db2ddSJeff Roberson * Free the children of a directory when the directory is discarded. 1232113db2ddSJeff Roberson */ 1233113db2ddSJeff Roberson static void 1234113db2ddSJeff Roberson ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 1235113db2ddSJeff Roberson { 1236113db2ddSJeff Roberson struct suj_ino *sino; 1237113db2ddSJeff Roberson struct direct *dp; 1238113db2ddSJeff Roberson off_t diroff; 1239113db2ddSJeff Roberson uint8_t *block; 1240113db2ddSJeff Roberson int skipparent; 124124d37c1eSJeff Roberson int isdotdot; 1242113db2ddSJeff Roberson int dpoff; 1243113db2ddSJeff Roberson int size; 1244113db2ddSJeff Roberson 1245113db2ddSJeff Roberson sino = ino_lookup(ino, 0); 1246113db2ddSJeff Roberson if (sino) 1247113db2ddSJeff Roberson skipparent = sino->si_skipparent; 1248113db2ddSJeff Roberson else 1249113db2ddSJeff Roberson skipparent = 0; 1250113db2ddSJeff Roberson size = lfragtosize(fs, frags); 1251113db2ddSJeff Roberson block = dblk_read(blk, size); 1252113db2ddSJeff Roberson dp = (struct direct *)&block[0]; 1253113db2ddSJeff Roberson for (dpoff = 0; dpoff < size && dp->d_reclen; dpoff += dp->d_reclen) { 1254113db2ddSJeff Roberson dp = (struct direct *)&block[dpoff]; 1255113db2ddSJeff Roberson if (dp->d_ino == 0 || dp->d_ino == WINO) 1256113db2ddSJeff Roberson continue; 1257113db2ddSJeff Roberson if (dp->d_namlen == 1 && dp->d_name[0] == '.') 1258113db2ddSJeff Roberson continue; 125924d37c1eSJeff Roberson isdotdot = dp->d_namlen == 2 && dp->d_name[0] == '.' && 1260113db2ddSJeff Roberson dp->d_name[1] == '.'; 126124d37c1eSJeff Roberson if (isdotdot && skipparent == 1) 1262113db2ddSJeff Roberson continue; 1263113db2ddSJeff Roberson if (debug) 1264623d7cb6SMatthew D Fleming printf("Directory %ju removing ino %ju name %s\n", 1265623d7cb6SMatthew D Fleming (uintmax_t)ino, (uintmax_t)dp->d_ino, dp->d_name); 1266113db2ddSJeff Roberson diroff = lblktosize(fs, lbn) + dpoff; 126724d37c1eSJeff Roberson ino_remref(ino, dp->d_ino, diroff, isdotdot); 1268113db2ddSJeff Roberson } 1269113db2ddSJeff Roberson } 1270113db2ddSJeff Roberson 1271113db2ddSJeff Roberson /* 1272113db2ddSJeff Roberson * Reclaim an inode, freeing all blocks and decrementing all children's 1273113db2ddSJeff Roberson * link counts. Free the inode back to the cg. 1274113db2ddSJeff Roberson */ 1275113db2ddSJeff Roberson static void 1276113db2ddSJeff Roberson ino_reclaim(union dinode *ip, ino_t ino, int mode) 1277113db2ddSJeff Roberson { 1278113db2ddSJeff Roberson uint32_t gen; 1279113db2ddSJeff Roberson 1280113db2ddSJeff Roberson if (ino == ROOTINO) 1281edad6026SXin LI err_suj("Attempting to free ROOTINO\n"); 1282113db2ddSJeff Roberson if (debug) 1283623d7cb6SMatthew D Fleming printf("Truncating and freeing ino %ju, nlink %d, mode %o\n", 1284623d7cb6SMatthew D Fleming (uintmax_t)ino, DIP(ip, di_nlink), DIP(ip, di_mode)); 1285113db2ddSJeff Roberson 1286113db2ddSJeff Roberson /* We are freeing an inode or directory. */ 1287113db2ddSJeff Roberson if ((DIP(ip, di_mode) & IFMT) == IFDIR) 1288113db2ddSJeff Roberson ino_visit(ip, ino, ino_free_children, 0); 1289113db2ddSJeff Roberson DIP_SET(ip, di_nlink, 0); 1290113db2ddSJeff Roberson ino_visit(ip, ino, blk_free_visit, VISIT_EXT | VISIT_INDIR); 1291113db2ddSJeff Roberson /* Here we have to clear the inode and release any blocks it holds. */ 1292113db2ddSJeff Roberson gen = DIP(ip, di_gen); 1293113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 1294113db2ddSJeff Roberson bzero(ip, sizeof(struct ufs1_dinode)); 1295113db2ddSJeff Roberson else 1296113db2ddSJeff Roberson bzero(ip, sizeof(struct ufs2_dinode)); 1297113db2ddSJeff Roberson DIP_SET(ip, di_gen, gen); 1298113db2ddSJeff Roberson ino_dirty(ino); 1299113db2ddSJeff Roberson ino_free(ino, mode); 1300113db2ddSJeff Roberson return; 1301113db2ddSJeff Roberson } 1302113db2ddSJeff Roberson 1303113db2ddSJeff Roberson /* 1304113db2ddSJeff Roberson * Adjust an inode's link count down by one when a directory goes away. 1305113db2ddSJeff Roberson */ 1306113db2ddSJeff Roberson static void 1307113db2ddSJeff Roberson ino_decr(ino_t ino) 1308113db2ddSJeff Roberson { 1309113db2ddSJeff Roberson union dinode *ip; 1310113db2ddSJeff Roberson int reqlink; 1311113db2ddSJeff Roberson int nlink; 1312113db2ddSJeff Roberson int mode; 1313113db2ddSJeff Roberson 1314113db2ddSJeff Roberson ip = ino_read(ino); 1315113db2ddSJeff Roberson nlink = DIP(ip, di_nlink); 1316113db2ddSJeff Roberson mode = DIP(ip, di_mode); 1317113db2ddSJeff Roberson if (nlink < 1) 1318edad6026SXin LI err_suj("Inode %d link count %d invalid\n", ino, nlink); 1319113db2ddSJeff Roberson if (mode == 0) 1320edad6026SXin LI err_suj("Inode %d has a link of %d with 0 mode\n", ino, nlink); 1321113db2ddSJeff Roberson nlink--; 1322113db2ddSJeff Roberson if ((mode & IFMT) == IFDIR) 1323113db2ddSJeff Roberson reqlink = 2; 1324113db2ddSJeff Roberson else 1325113db2ddSJeff Roberson reqlink = 1; 1326113db2ddSJeff Roberson if (nlink < reqlink) { 1327113db2ddSJeff Roberson if (debug) 1328623d7cb6SMatthew D Fleming printf("ino %ju not enough links to live %d < %d\n", 1329623d7cb6SMatthew D Fleming (uintmax_t)ino, nlink, reqlink); 1330113db2ddSJeff Roberson ino_reclaim(ip, ino, mode); 1331113db2ddSJeff Roberson return; 1332113db2ddSJeff Roberson } 1333113db2ddSJeff Roberson DIP_SET(ip, di_nlink, nlink); 1334113db2ddSJeff Roberson ino_dirty(ino); 1335113db2ddSJeff Roberson } 1336113db2ddSJeff Roberson 1337113db2ddSJeff Roberson /* 1338113db2ddSJeff Roberson * Adjust the inode link count to 'nlink'. If the count reaches zero 1339113db2ddSJeff Roberson * free it. 1340113db2ddSJeff Roberson */ 1341113db2ddSJeff Roberson static void 1342113db2ddSJeff Roberson ino_adjust(struct suj_ino *sino) 1343113db2ddSJeff Roberson { 1344113db2ddSJeff Roberson struct jrefrec *rrec; 1345113db2ddSJeff Roberson struct suj_rec *srec; 1346113db2ddSJeff Roberson struct suj_ino *stmp; 1347113db2ddSJeff Roberson union dinode *ip; 1348113db2ddSJeff Roberson nlink_t nlink; 134924d37c1eSJeff Roberson int recmode; 1350113db2ddSJeff Roberson int reqlink; 135124d37c1eSJeff Roberson int isdot; 1352113db2ddSJeff Roberson int mode; 1353113db2ddSJeff Roberson ino_t ino; 1354113db2ddSJeff Roberson 1355113db2ddSJeff Roberson nlink = sino->si_nlink; 1356113db2ddSJeff Roberson ino = sino->si_ino; 135724d37c1eSJeff Roberson mode = sino->si_mode & IFMT; 135824d37c1eSJeff Roberson /* 135924d37c1eSJeff Roberson * If it's a directory with no dot links, it was truncated before 136024d37c1eSJeff Roberson * the name was cleared. We need to clear the dirent that 136124d37c1eSJeff Roberson * points at it. 136224d37c1eSJeff Roberson */ 136324d37c1eSJeff Roberson if (mode == IFDIR && nlink == 1 && sino->si_dotlinks == 0) { 136424d37c1eSJeff Roberson sino->si_nlink = nlink = 0; 136524d37c1eSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 136624d37c1eSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 136724d37c1eSJeff Roberson if (ino_isat(rrec->jr_parent, rrec->jr_diroff, ino, 136824d37c1eSJeff Roberson &recmode, &isdot) == 0) 136924d37c1eSJeff Roberson continue; 137024d37c1eSJeff Roberson ino_clrat(rrec->jr_parent, rrec->jr_diroff, ino); 137124d37c1eSJeff Roberson break; 137224d37c1eSJeff Roberson } 137324d37c1eSJeff Roberson if (srec == NULL) 1374623d7cb6SMatthew D Fleming errx(1, "Directory %ju name not found", (uintmax_t)ino); 137524d37c1eSJeff Roberson } 1376113db2ddSJeff Roberson /* 1377113db2ddSJeff Roberson * If it's a directory with no real names pointing to it go ahead 1378113db2ddSJeff Roberson * and truncate it. This will free any children. 1379113db2ddSJeff Roberson */ 138024d37c1eSJeff Roberson if (mode == IFDIR && nlink - sino->si_dotlinks == 0) { 1381113db2ddSJeff Roberson sino->si_nlink = nlink = 0; 1382113db2ddSJeff Roberson /* 1383113db2ddSJeff Roberson * Mark any .. links so they know not to free this inode 1384113db2ddSJeff Roberson * when they are removed. 1385113db2ddSJeff Roberson */ 1386113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 1387113db2ddSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 1388113db2ddSJeff Roberson if (rrec->jr_diroff == DOTDOT_OFFSET) { 1389113db2ddSJeff Roberson stmp = ino_lookup(rrec->jr_parent, 0); 1390113db2ddSJeff Roberson if (stmp) 1391113db2ddSJeff Roberson ino_setskip(stmp, ino); 1392113db2ddSJeff Roberson } 1393113db2ddSJeff Roberson } 1394113db2ddSJeff Roberson } 1395113db2ddSJeff Roberson ip = ino_read(ino); 1396113db2ddSJeff Roberson mode = DIP(ip, di_mode) & IFMT; 1397113db2ddSJeff Roberson if (nlink > LINK_MAX) 1398a1c9ec3cSMatthew D Fleming err_suj("ino %ju nlink manipulation error, new %d, old %d\n", 1399a1c9ec3cSMatthew D Fleming (uintmax_t)ino, nlink, DIP(ip, di_nlink)); 1400113db2ddSJeff Roberson if (debug) 1401623d7cb6SMatthew D Fleming printf("Adjusting ino %ju, nlink %d, old link %d lastmode %o\n", 1402623d7cb6SMatthew D Fleming (uintmax_t)ino, nlink, DIP(ip, di_nlink), sino->si_mode); 1403113db2ddSJeff Roberson if (mode == 0) { 1404113db2ddSJeff Roberson if (debug) 1405623d7cb6SMatthew D Fleming printf("ino %ju, zero inode freeing bitmap\n", 1406623d7cb6SMatthew D Fleming (uintmax_t)ino); 1407113db2ddSJeff Roberson ino_free(ino, sino->si_mode); 1408113db2ddSJeff Roberson return; 1409113db2ddSJeff Roberson } 1410113db2ddSJeff Roberson /* XXX Should be an assert? */ 1411113db2ddSJeff Roberson if (mode != sino->si_mode && debug) 1412623d7cb6SMatthew D Fleming printf("ino %ju, mode %o != %o\n", 1413623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, sino->si_mode); 1414113db2ddSJeff Roberson if ((mode & IFMT) == IFDIR) 1415113db2ddSJeff Roberson reqlink = 2; 1416113db2ddSJeff Roberson else 1417113db2ddSJeff Roberson reqlink = 1; 1418113db2ddSJeff Roberson /* If the inode doesn't have enough links to live, free it. */ 1419113db2ddSJeff Roberson if (nlink < reqlink) { 1420113db2ddSJeff Roberson if (debug) 1421623d7cb6SMatthew D Fleming printf("ino %ju not enough links to live %d < %d\n", 1422623d7cb6SMatthew D Fleming (uintmax_t)ino, nlink, reqlink); 1423113db2ddSJeff Roberson ino_reclaim(ip, ino, mode); 1424113db2ddSJeff Roberson return; 1425113db2ddSJeff Roberson } 1426113db2ddSJeff Roberson /* If required write the updated link count. */ 1427113db2ddSJeff Roberson if (DIP(ip, di_nlink) == nlink) { 1428113db2ddSJeff Roberson if (debug) 1429623d7cb6SMatthew D Fleming printf("ino %ju, link matches, skipping.\n", 1430623d7cb6SMatthew D Fleming (uintmax_t)ino); 1431113db2ddSJeff Roberson return; 1432113db2ddSJeff Roberson } 1433113db2ddSJeff Roberson DIP_SET(ip, di_nlink, nlink); 1434113db2ddSJeff Roberson ino_dirty(ino); 1435113db2ddSJeff Roberson } 1436113db2ddSJeff Roberson 1437113db2ddSJeff Roberson /* 1438113db2ddSJeff Roberson * Truncate some or all blocks in an indirect, freeing any that are required 1439113db2ddSJeff Roberson * and zeroing the indirect. 1440113db2ddSJeff Roberson */ 1441113db2ddSJeff Roberson static void 1442113db2ddSJeff Roberson indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn) 1443113db2ddSJeff Roberson { 1444113db2ddSJeff Roberson ufs2_daddr_t *bap2; 1445113db2ddSJeff Roberson ufs1_daddr_t *bap1; 1446113db2ddSJeff Roberson ufs_lbn_t lbnadd; 1447113db2ddSJeff Roberson ufs2_daddr_t nblk; 1448113db2ddSJeff Roberson ufs_lbn_t next; 1449113db2ddSJeff Roberson ufs_lbn_t nlbn; 1450113db2ddSJeff Roberson int dirty; 1451113db2ddSJeff Roberson int level; 1452113db2ddSJeff Roberson int i; 1453113db2ddSJeff Roberson 1454113db2ddSJeff Roberson if (blk == 0) 1455113db2ddSJeff Roberson return; 1456113db2ddSJeff Roberson dirty = 0; 1457113db2ddSJeff Roberson level = lbn_level(lbn); 1458113db2ddSJeff Roberson if (level == -1) 1459edad6026SXin LI err_suj("Invalid level for lbn %jd\n", lbn); 1460113db2ddSJeff Roberson lbnadd = 1; 1461113db2ddSJeff Roberson for (i = level; i > 0; i--) 1462113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 1463113db2ddSJeff Roberson bap1 = (void *)dblk_read(blk, fs->fs_bsize); 1464113db2ddSJeff Roberson bap2 = (void *)bap1; 1465113db2ddSJeff Roberson for (i = 0; i < NINDIR(fs); i++) { 1466113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 1467113db2ddSJeff Roberson nblk = *bap1++; 1468113db2ddSJeff Roberson else 1469113db2ddSJeff Roberson nblk = *bap2++; 1470113db2ddSJeff Roberson if (nblk == 0) 1471113db2ddSJeff Roberson continue; 1472113db2ddSJeff Roberson if (level != 0) { 1473113db2ddSJeff Roberson nlbn = (lbn + 1) - (i * lbnadd); 1474113db2ddSJeff Roberson /* 1475113db2ddSJeff Roberson * Calculate the lbn of the next indirect to 1476113db2ddSJeff Roberson * determine if any of this indirect must be 1477113db2ddSJeff Roberson * reclaimed. 1478113db2ddSJeff Roberson */ 1479113db2ddSJeff Roberson next = -(lbn + level) + ((i+1) * lbnadd); 1480113db2ddSJeff Roberson if (next <= lastlbn) 1481113db2ddSJeff Roberson continue; 1482113db2ddSJeff Roberson indir_trunc(ino, nlbn, nblk, lastlbn); 1483113db2ddSJeff Roberson /* If all of this indirect was reclaimed, free it. */ 1484113db2ddSJeff Roberson nlbn = next - lbnadd; 1485113db2ddSJeff Roberson if (nlbn < lastlbn) 1486113db2ddSJeff Roberson continue; 1487113db2ddSJeff Roberson } else { 1488113db2ddSJeff Roberson nlbn = -lbn + i * lbnadd; 1489113db2ddSJeff Roberson if (nlbn < lastlbn) 1490113db2ddSJeff Roberson continue; 1491113db2ddSJeff Roberson } 1492113db2ddSJeff Roberson dirty = 1; 1493113db2ddSJeff Roberson blk_free(nblk, 0, fs->fs_frag); 1494113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 1495113db2ddSJeff Roberson *(bap1 - 1) = 0; 1496113db2ddSJeff Roberson else 1497113db2ddSJeff Roberson *(bap2 - 1) = 0; 1498113db2ddSJeff Roberson } 1499113db2ddSJeff Roberson if (dirty) 1500113db2ddSJeff Roberson dblk_dirty(blk); 1501113db2ddSJeff Roberson } 1502113db2ddSJeff Roberson 1503113db2ddSJeff Roberson /* 1504113db2ddSJeff Roberson * Truncate an inode to the minimum of the given size or the last populated 1505113db2ddSJeff Roberson * block after any over size have been discarded. The kernel would allocate 1506113db2ddSJeff Roberson * the last block in the file but fsck does not and neither do we. This 1507113db2ddSJeff Roberson * code never extends files, only shrinks them. 1508113db2ddSJeff Roberson */ 1509113db2ddSJeff Roberson static void 1510113db2ddSJeff Roberson ino_trunc(ino_t ino, off_t size) 1511113db2ddSJeff Roberson { 1512113db2ddSJeff Roberson union dinode *ip; 1513113db2ddSJeff Roberson ufs2_daddr_t bn; 1514113db2ddSJeff Roberson uint64_t totalfrags; 1515113db2ddSJeff Roberson ufs_lbn_t nextlbn; 1516113db2ddSJeff Roberson ufs_lbn_t lastlbn; 1517113db2ddSJeff Roberson ufs_lbn_t tmpval; 1518113db2ddSJeff Roberson ufs_lbn_t lbn; 1519113db2ddSJeff Roberson ufs_lbn_t i; 1520113db2ddSJeff Roberson int frags; 1521113db2ddSJeff Roberson off_t cursize; 1522113db2ddSJeff Roberson off_t off; 1523113db2ddSJeff Roberson int mode; 1524113db2ddSJeff Roberson 1525113db2ddSJeff Roberson ip = ino_read(ino); 1526113db2ddSJeff Roberson mode = DIP(ip, di_mode) & IFMT; 1527113db2ddSJeff Roberson cursize = DIP(ip, di_size); 1528113db2ddSJeff Roberson if (debug) 1529623d7cb6SMatthew D Fleming printf("Truncating ino %ju, mode %o to size %jd from size %jd\n", 1530623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, size, cursize); 1531113db2ddSJeff Roberson 1532113db2ddSJeff Roberson /* Skip datablocks for short links and devices. */ 1533113db2ddSJeff Roberson if (mode == 0 || mode == IFBLK || mode == IFCHR || 1534113db2ddSJeff Roberson (mode == IFLNK && cursize < fs->fs_maxsymlinklen)) 1535113db2ddSJeff Roberson return; 1536113db2ddSJeff Roberson /* Don't extend. */ 1537113db2ddSJeff Roberson if (size > cursize) 1538113db2ddSJeff Roberson size = cursize; 1539113db2ddSJeff Roberson lastlbn = lblkno(fs, blkroundup(fs, size)); 1540113db2ddSJeff Roberson for (i = lastlbn; i < NDADDR; i++) { 1541113db2ddSJeff Roberson if (DIP(ip, di_db[i]) == 0) 1542113db2ddSJeff Roberson continue; 1543113db2ddSJeff Roberson frags = sblksize(fs, cursize, i); 1544113db2ddSJeff Roberson frags = numfrags(fs, frags); 1545113db2ddSJeff Roberson blk_free(DIP(ip, di_db[i]), 0, frags); 1546113db2ddSJeff Roberson DIP_SET(ip, di_db[i], 0); 1547113db2ddSJeff Roberson } 1548113db2ddSJeff Roberson /* 1549113db2ddSJeff Roberson * Follow indirect blocks, freeing anything required. 1550113db2ddSJeff Roberson */ 1551113db2ddSJeff Roberson for (i = 0, tmpval = NINDIR(fs), lbn = NDADDR; i < NIADDR; i++, 1552113db2ddSJeff Roberson lbn = nextlbn) { 1553113db2ddSJeff Roberson nextlbn = lbn + tmpval; 1554113db2ddSJeff Roberson tmpval *= NINDIR(fs); 1555113db2ddSJeff Roberson /* If we're not freeing any in this indirect range skip it. */ 1556113db2ddSJeff Roberson if (lastlbn >= nextlbn) 1557113db2ddSJeff Roberson continue; 1558113db2ddSJeff Roberson if (DIP(ip, di_ib[i]) == 0) 1559113db2ddSJeff Roberson continue; 1560113db2ddSJeff Roberson indir_trunc(ino, -lbn - i, DIP(ip, di_ib[i]), lastlbn); 1561113db2ddSJeff Roberson /* If we freed everything in this indirect free the indir. */ 1562113db2ddSJeff Roberson if (lastlbn > lbn) 1563113db2ddSJeff Roberson continue; 1564113db2ddSJeff Roberson blk_free(DIP(ip, di_ib[i]), 0, frags); 1565113db2ddSJeff Roberson DIP_SET(ip, di_ib[i], 0); 1566113db2ddSJeff Roberson } 1567113db2ddSJeff Roberson ino_dirty(ino); 1568113db2ddSJeff Roberson /* 1569113db2ddSJeff Roberson * Now that we've freed any whole blocks that exceed the desired 1570113db2ddSJeff Roberson * truncation size, figure out how many blocks remain and what the 1571113db2ddSJeff Roberson * last populated lbn is. We will set the size to this last lbn 1572113db2ddSJeff Roberson * rather than worrying about allocating the final lbn as the kernel 1573113db2ddSJeff Roberson * would've done. This is consistent with normal fsck behavior. 1574113db2ddSJeff Roberson */ 1575113db2ddSJeff Roberson visitlbn = 0; 1576113db2ddSJeff Roberson totalfrags = ino_visit(ip, ino, null_visit, VISIT_INDIR | VISIT_EXT); 1577113db2ddSJeff Roberson if (size > lblktosize(fs, visitlbn + 1)) 1578113db2ddSJeff Roberson size = lblktosize(fs, visitlbn + 1); 1579113db2ddSJeff Roberson /* 1580113db2ddSJeff Roberson * If we're truncating direct blocks we have to adjust frags 1581113db2ddSJeff Roberson * accordingly. 1582113db2ddSJeff Roberson */ 1583113db2ddSJeff Roberson if (visitlbn < NDADDR && totalfrags) { 1584113db2ddSJeff Roberson long oldspace, newspace; 1585113db2ddSJeff Roberson 1586113db2ddSJeff Roberson bn = DIP(ip, di_db[visitlbn]); 1587113db2ddSJeff Roberson if (bn == 0) 1588623d7cb6SMatthew D Fleming err_suj("Bad blk at ino %ju lbn %jd\n", 1589623d7cb6SMatthew D Fleming (uintmax_t)ino, visitlbn); 1590113db2ddSJeff Roberson oldspace = sblksize(fs, cursize, visitlbn); 1591113db2ddSJeff Roberson newspace = sblksize(fs, size, visitlbn); 1592113db2ddSJeff Roberson if (oldspace != newspace) { 1593113db2ddSJeff Roberson bn += numfrags(fs, newspace); 1594113db2ddSJeff Roberson frags = numfrags(fs, oldspace - newspace); 1595113db2ddSJeff Roberson blk_free(bn, 0, frags); 1596113db2ddSJeff Roberson totalfrags -= frags; 1597113db2ddSJeff Roberson } 1598113db2ddSJeff Roberson } 1599113db2ddSJeff Roberson DIP_SET(ip, di_blocks, fsbtodb(fs, totalfrags)); 1600113db2ddSJeff Roberson DIP_SET(ip, di_size, size); 1601113db2ddSJeff Roberson /* 1602113db2ddSJeff Roberson * If we've truncated into the middle of a block or frag we have 1603113db2ddSJeff Roberson * to zero it here. Otherwise the file could extend into 1604113db2ddSJeff Roberson * uninitialized space later. 1605113db2ddSJeff Roberson */ 1606113db2ddSJeff Roberson off = blkoff(fs, size); 1607280e091aSJeff Roberson if (off && DIP(ip, di_mode) != IFDIR) { 1608113db2ddSJeff Roberson uint8_t *buf; 1609113db2ddSJeff Roberson long clrsize; 1610113db2ddSJeff Roberson 1611113db2ddSJeff Roberson bn = ino_blkatoff(ip, ino, visitlbn, &frags); 1612113db2ddSJeff Roberson if (bn == 0) 1613623d7cb6SMatthew D Fleming err_suj("Block missing from ino %ju at lbn %jd\n", 1614623d7cb6SMatthew D Fleming (uintmax_t)ino, visitlbn); 1615113db2ddSJeff Roberson clrsize = frags * fs->fs_fsize; 1616113db2ddSJeff Roberson buf = dblk_read(bn, clrsize); 1617113db2ddSJeff Roberson clrsize -= off; 1618113db2ddSJeff Roberson buf += off; 1619113db2ddSJeff Roberson bzero(buf, clrsize); 1620113db2ddSJeff Roberson dblk_dirty(bn); 1621113db2ddSJeff Roberson } 1622113db2ddSJeff Roberson return; 1623113db2ddSJeff Roberson } 1624113db2ddSJeff Roberson 1625113db2ddSJeff Roberson /* 1626113db2ddSJeff Roberson * Process records available for one inode and determine whether the 1627113db2ddSJeff Roberson * link count is correct or needs adjusting. 1628113db2ddSJeff Roberson */ 1629113db2ddSJeff Roberson static void 1630113db2ddSJeff Roberson ino_check(struct suj_ino *sino) 1631113db2ddSJeff Roberson { 1632113db2ddSJeff Roberson struct suj_rec *srec; 1633113db2ddSJeff Roberson struct jrefrec *rrec; 1634113db2ddSJeff Roberson nlink_t dotlinks; 1635113db2ddSJeff Roberson int newlinks; 1636113db2ddSJeff Roberson int removes; 1637113db2ddSJeff Roberson int nlink; 1638113db2ddSJeff Roberson ino_t ino; 1639113db2ddSJeff Roberson int isdot; 1640113db2ddSJeff Roberson int isat; 1641113db2ddSJeff Roberson int mode; 1642113db2ddSJeff Roberson 1643113db2ddSJeff Roberson if (sino->si_hasrecs == 0) 1644113db2ddSJeff Roberson return; 1645113db2ddSJeff Roberson ino = sino->si_ino; 1646113db2ddSJeff Roberson rrec = (struct jrefrec *)TAILQ_FIRST(&sino->si_recs)->sr_rec; 1647113db2ddSJeff Roberson nlink = rrec->jr_nlink; 1648113db2ddSJeff Roberson newlinks = 0; 1649113db2ddSJeff Roberson dotlinks = 0; 1650113db2ddSJeff Roberson removes = sino->si_nlinkadj; 1651113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 1652113db2ddSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 1653113db2ddSJeff Roberson isat = ino_isat(rrec->jr_parent, rrec->jr_diroff, 1654113db2ddSJeff Roberson rrec->jr_ino, &mode, &isdot); 1655113db2ddSJeff Roberson if (isat && (mode & IFMT) != (rrec->jr_mode & IFMT)) 1656edad6026SXin LI err_suj("Inode mode/directory type mismatch %o != %o\n", 1657113db2ddSJeff Roberson mode, rrec->jr_mode); 1658113db2ddSJeff Roberson if (debug) 1659623d7cb6SMatthew D Fleming printf("jrefrec: op %d ino %ju, nlink %d, parent %d, " 1660113db2ddSJeff Roberson "diroff %jd, mode %o, isat %d, isdot %d\n", 1661623d7cb6SMatthew D Fleming rrec->jr_op, (uintmax_t)rrec->jr_ino, 1662623d7cb6SMatthew D Fleming rrec->jr_nlink, rrec->jr_parent, rrec->jr_diroff, 1663623d7cb6SMatthew D Fleming rrec->jr_mode, isat, isdot); 1664113db2ddSJeff Roberson mode = rrec->jr_mode & IFMT; 1665113db2ddSJeff Roberson if (rrec->jr_op == JOP_REMREF) 1666113db2ddSJeff Roberson removes++; 1667113db2ddSJeff Roberson newlinks += isat; 1668113db2ddSJeff Roberson if (isdot) 1669113db2ddSJeff Roberson dotlinks += isat; 1670113db2ddSJeff Roberson } 1671113db2ddSJeff Roberson /* 1672113db2ddSJeff Roberson * The number of links that remain are the starting link count 1673113db2ddSJeff Roberson * subtracted by the total number of removes with the total 1674113db2ddSJeff Roberson * links discovered back in. An incomplete remove thus 1675113db2ddSJeff Roberson * makes no change to the link count but an add increases 1676113db2ddSJeff Roberson * by one. 1677113db2ddSJeff Roberson */ 1678113db2ddSJeff Roberson if (debug) 1679623d7cb6SMatthew D Fleming printf("ino %ju nlink %d newlinks %d removes %d dotlinks %d\n", 1680623d7cb6SMatthew D Fleming (uintmax_t)ino, nlink, newlinks, removes, dotlinks); 1681113db2ddSJeff Roberson nlink += newlinks; 1682113db2ddSJeff Roberson nlink -= removes; 1683113db2ddSJeff Roberson sino->si_linkadj = 1; 1684113db2ddSJeff Roberson sino->si_nlink = nlink; 1685113db2ddSJeff Roberson sino->si_dotlinks = dotlinks; 1686113db2ddSJeff Roberson sino->si_mode = mode; 1687113db2ddSJeff Roberson ino_adjust(sino); 1688113db2ddSJeff Roberson } 1689113db2ddSJeff Roberson 1690113db2ddSJeff Roberson /* 1691113db2ddSJeff Roberson * Process records available for one block and determine whether it is 1692113db2ddSJeff Roberson * still allocated and whether the owning inode needs to be updated or 1693113db2ddSJeff Roberson * a free completed. 1694113db2ddSJeff Roberson */ 1695113db2ddSJeff Roberson static void 1696113db2ddSJeff Roberson blk_check(struct suj_blk *sblk) 1697113db2ddSJeff Roberson { 1698113db2ddSJeff Roberson struct suj_rec *srec; 1699113db2ddSJeff Roberson struct jblkrec *brec; 1700113db2ddSJeff Roberson struct suj_ino *sino; 1701113db2ddSJeff Roberson ufs2_daddr_t blk; 1702113db2ddSJeff Roberson int mask; 1703113db2ddSJeff Roberson int frags; 1704113db2ddSJeff Roberson int isat; 1705113db2ddSJeff Roberson 1706113db2ddSJeff Roberson /* 1707113db2ddSJeff Roberson * Each suj_blk actually contains records for any fragments in that 1708113db2ddSJeff Roberson * block. As a result we must evaluate each record individually. 1709113db2ddSJeff Roberson */ 1710113db2ddSJeff Roberson sino = NULL; 1711113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 1712113db2ddSJeff Roberson brec = (struct jblkrec *)srec->sr_rec; 1713113db2ddSJeff Roberson frags = brec->jb_frags; 1714113db2ddSJeff Roberson blk = brec->jb_blkno + brec->jb_oldfrags; 1715113db2ddSJeff Roberson isat = blk_isat(brec->jb_ino, brec->jb_lbn, blk, &frags); 1716113db2ddSJeff Roberson if (sino == NULL || sino->si_ino != brec->jb_ino) { 1717113db2ddSJeff Roberson sino = ino_lookup(brec->jb_ino, 1); 1718113db2ddSJeff Roberson sino->si_blkadj = 1; 1719113db2ddSJeff Roberson } 1720113db2ddSJeff Roberson if (debug) 1721623d7cb6SMatthew D Fleming printf("op %d blk %jd ino %ju lbn %jd frags %d isat %d (%d)\n", 1722623d7cb6SMatthew D Fleming brec->jb_op, blk, (uintmax_t)brec->jb_ino, 1723623d7cb6SMatthew D Fleming brec->jb_lbn, brec->jb_frags, isat, frags); 1724113db2ddSJeff Roberson /* 1725113db2ddSJeff Roberson * If we found the block at this address we still have to 1726113db2ddSJeff Roberson * determine if we need to free the tail end that was 1727113db2ddSJeff Roberson * added by adding contiguous fragments from the same block. 1728113db2ddSJeff Roberson */ 1729113db2ddSJeff Roberson if (isat == 1) { 1730113db2ddSJeff Roberson if (frags == brec->jb_frags) 1731113db2ddSJeff Roberson continue; 1732113db2ddSJeff Roberson mask = blk_freemask(blk, brec->jb_ino, brec->jb_lbn, 1733113db2ddSJeff Roberson brec->jb_frags); 1734113db2ddSJeff Roberson mask >>= frags; 1735113db2ddSJeff Roberson blk += frags; 1736113db2ddSJeff Roberson frags = brec->jb_frags - frags; 1737113db2ddSJeff Roberson blk_free(blk, mask, frags); 1738113db2ddSJeff Roberson continue; 1739113db2ddSJeff Roberson } 1740113db2ddSJeff Roberson /* 1741113db2ddSJeff Roberson * The block wasn't found, attempt to free it. It won't be 1742113db2ddSJeff Roberson * freed if it was actually reallocated. If this was an 1743113db2ddSJeff Roberson * allocation we don't want to follow indirects as they 1744113db2ddSJeff Roberson * may not be written yet. Any children of the indirect will 1745113db2ddSJeff Roberson * have their own records. If it's a free we need to 1746113db2ddSJeff Roberson * recursively free children. 1747113db2ddSJeff Roberson */ 1748113db2ddSJeff Roberson blk_free_lbn(blk, brec->jb_ino, brec->jb_lbn, brec->jb_frags, 1749113db2ddSJeff Roberson brec->jb_op == JOP_FREEBLK); 1750113db2ddSJeff Roberson } 1751113db2ddSJeff Roberson } 1752113db2ddSJeff Roberson 1753113db2ddSJeff Roberson /* 1754113db2ddSJeff Roberson * Walk the list of inode records for this cg and resolve moved and duplicate 1755113db2ddSJeff Roberson * inode references now that we have a complete picture. 1756113db2ddSJeff Roberson */ 1757113db2ddSJeff Roberson static void 1758113db2ddSJeff Roberson cg_build(struct suj_cg *sc) 1759113db2ddSJeff Roberson { 1760113db2ddSJeff Roberson struct suj_ino *sino; 1761113db2ddSJeff Roberson int i; 1762113db2ddSJeff Roberson 1763113db2ddSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) 1764113db2ddSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) 1765113db2ddSJeff Roberson ino_build(sino); 1766113db2ddSJeff Roberson } 1767113db2ddSJeff Roberson 1768113db2ddSJeff Roberson /* 1769113db2ddSJeff Roberson * Handle inodes requiring truncation. This must be done prior to 1770113db2ddSJeff Roberson * looking up any inodes in directories. 1771113db2ddSJeff Roberson */ 1772113db2ddSJeff Roberson static void 1773113db2ddSJeff Roberson cg_trunc(struct suj_cg *sc) 1774113db2ddSJeff Roberson { 1775113db2ddSJeff Roberson struct suj_ino *sino; 1776113db2ddSJeff Roberson int i; 1777113db2ddSJeff Roberson 1778280e091aSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) { 1779280e091aSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) { 1780113db2ddSJeff Roberson if (sino->si_trunc) { 1781113db2ddSJeff Roberson ino_trunc(sino->si_ino, 1782113db2ddSJeff Roberson sino->si_trunc->jt_size); 1783280e091aSJeff Roberson sino->si_blkadj = 0; 1784113db2ddSJeff Roberson sino->si_trunc = NULL; 1785113db2ddSJeff Roberson } 1786280e091aSJeff Roberson if (sino->si_blkadj) 1787280e091aSJeff Roberson ino_adjblks(sino); 1788280e091aSJeff Roberson } 1789280e091aSJeff Roberson } 1790113db2ddSJeff Roberson } 1791113db2ddSJeff Roberson 1792364e7245SKonstantin Belousov static void 1793364e7245SKonstantin Belousov cg_adj_blk(struct suj_cg *sc) 1794364e7245SKonstantin Belousov { 1795364e7245SKonstantin Belousov struct suj_ino *sino; 1796364e7245SKonstantin Belousov int i; 1797364e7245SKonstantin Belousov 1798364e7245SKonstantin Belousov for (i = 0; i < SUJ_HASHSIZE; i++) { 1799364e7245SKonstantin Belousov LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) { 1800364e7245SKonstantin Belousov if (sino->si_blkadj) 1801364e7245SKonstantin Belousov ino_adjblks(sino); 1802364e7245SKonstantin Belousov } 1803364e7245SKonstantin Belousov } 1804364e7245SKonstantin Belousov } 1805364e7245SKonstantin Belousov 1806113db2ddSJeff Roberson /* 1807113db2ddSJeff Roberson * Free any partially allocated blocks and then resolve inode block 1808113db2ddSJeff Roberson * counts. 1809113db2ddSJeff Roberson */ 1810113db2ddSJeff Roberson static void 1811113db2ddSJeff Roberson cg_check_blk(struct suj_cg *sc) 1812113db2ddSJeff Roberson { 1813113db2ddSJeff Roberson struct suj_blk *sblk; 1814113db2ddSJeff Roberson int i; 1815113db2ddSJeff Roberson 1816113db2ddSJeff Roberson 1817113db2ddSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) 1818113db2ddSJeff Roberson LIST_FOREACH(sblk, &sc->sc_blkhash[i], sb_next) 1819113db2ddSJeff Roberson blk_check(sblk); 1820113db2ddSJeff Roberson } 1821113db2ddSJeff Roberson 1822113db2ddSJeff Roberson /* 1823113db2ddSJeff Roberson * Walk the list of inode records for this cg, recovering any 1824113db2ddSJeff Roberson * changes which were not complete at the time of crash. 1825113db2ddSJeff Roberson */ 1826113db2ddSJeff Roberson static void 1827113db2ddSJeff Roberson cg_check_ino(struct suj_cg *sc) 1828113db2ddSJeff Roberson { 1829113db2ddSJeff Roberson struct suj_ino *sino; 1830113db2ddSJeff Roberson int i; 1831113db2ddSJeff Roberson 1832113db2ddSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) 1833113db2ddSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) 1834113db2ddSJeff Roberson ino_check(sino); 1835113db2ddSJeff Roberson } 1836113db2ddSJeff Roberson 1837113db2ddSJeff Roberson /* 1838113db2ddSJeff Roberson * Write a potentially dirty cg. Recalculate the summary information and 1839113db2ddSJeff Roberson * update the superblock summary. 1840113db2ddSJeff Roberson */ 1841113db2ddSJeff Roberson static void 1842113db2ddSJeff Roberson cg_write(struct suj_cg *sc) 1843113db2ddSJeff Roberson { 1844113db2ddSJeff Roberson ufs1_daddr_t fragno, cgbno, maxbno; 1845113db2ddSJeff Roberson u_int8_t *blksfree; 1846113db2ddSJeff Roberson struct cg *cgp; 1847113db2ddSJeff Roberson int blk; 1848113db2ddSJeff Roberson int i; 1849113db2ddSJeff Roberson 1850113db2ddSJeff Roberson if (sc->sc_dirty == 0) 1851113db2ddSJeff Roberson return; 1852113db2ddSJeff Roberson /* 1853113db2ddSJeff Roberson * Fix the frag and cluster summary. 1854113db2ddSJeff Roberson */ 1855113db2ddSJeff Roberson cgp = sc->sc_cgp; 1856113db2ddSJeff Roberson cgp->cg_cs.cs_nbfree = 0; 1857113db2ddSJeff Roberson cgp->cg_cs.cs_nffree = 0; 1858113db2ddSJeff Roberson bzero(&cgp->cg_frsum, sizeof(cgp->cg_frsum)); 1859113db2ddSJeff Roberson maxbno = fragstoblks(fs, fs->fs_fpg); 1860113db2ddSJeff Roberson if (fs->fs_contigsumsize > 0) { 1861113db2ddSJeff Roberson for (i = 1; i <= fs->fs_contigsumsize; i++) 1862113db2ddSJeff Roberson cg_clustersum(cgp)[i] = 0; 1863113db2ddSJeff Roberson bzero(cg_clustersfree(cgp), howmany(maxbno, CHAR_BIT)); 1864113db2ddSJeff Roberson } 1865113db2ddSJeff Roberson blksfree = cg_blksfree(cgp); 1866113db2ddSJeff Roberson for (cgbno = 0; cgbno < maxbno; cgbno++) { 1867113db2ddSJeff Roberson if (ffs_isfreeblock(fs, blksfree, cgbno)) 1868113db2ddSJeff Roberson continue; 1869113db2ddSJeff Roberson if (ffs_isblock(fs, blksfree, cgbno)) { 1870113db2ddSJeff Roberson ffs_clusteracct(fs, cgp, cgbno, 1); 1871113db2ddSJeff Roberson cgp->cg_cs.cs_nbfree++; 1872113db2ddSJeff Roberson continue; 1873113db2ddSJeff Roberson } 1874113db2ddSJeff Roberson fragno = blkstofrags(fs, cgbno); 1875113db2ddSJeff Roberson blk = blkmap(fs, blksfree, fragno); 1876113db2ddSJeff Roberson ffs_fragacct(fs, blk, cgp->cg_frsum, 1); 1877113db2ddSJeff Roberson for (i = 0; i < fs->fs_frag; i++) 1878113db2ddSJeff Roberson if (isset(blksfree, fragno + i)) 1879113db2ddSJeff Roberson cgp->cg_cs.cs_nffree++; 1880113db2ddSJeff Roberson } 1881113db2ddSJeff Roberson /* 1882113db2ddSJeff Roberson * Update the superblock cg summary from our now correct values 1883113db2ddSJeff Roberson * before writing the block. 1884113db2ddSJeff Roberson */ 1885113db2ddSJeff Roberson fs->fs_cs(fs, sc->sc_cgx) = cgp->cg_cs; 1886113db2ddSJeff Roberson if (bwrite(disk, fsbtodb(fs, cgtod(fs, sc->sc_cgx)), sc->sc_cgbuf, 1887113db2ddSJeff Roberson fs->fs_bsize) == -1) 1888edad6026SXin LI err_suj("Unable to write cylinder group %d\n", sc->sc_cgx); 1889113db2ddSJeff Roberson } 1890113db2ddSJeff Roberson 1891113db2ddSJeff Roberson /* 1892113db2ddSJeff Roberson * Write out any modified inodes. 1893113db2ddSJeff Roberson */ 1894113db2ddSJeff Roberson static void 1895113db2ddSJeff Roberson cg_write_inos(struct suj_cg *sc) 1896113db2ddSJeff Roberson { 1897113db2ddSJeff Roberson struct ino_blk *iblk; 1898113db2ddSJeff Roberson int i; 1899113db2ddSJeff Roberson 1900113db2ddSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) 1901113db2ddSJeff Roberson LIST_FOREACH(iblk, &sc->sc_iblkhash[i], ib_next) 1902113db2ddSJeff Roberson if (iblk->ib_dirty) 1903113db2ddSJeff Roberson iblk_write(iblk); 1904113db2ddSJeff Roberson } 1905113db2ddSJeff Roberson 1906113db2ddSJeff Roberson static void 1907113db2ddSJeff Roberson cg_apply(void (*apply)(struct suj_cg *)) 1908113db2ddSJeff Roberson { 1909113db2ddSJeff Roberson struct suj_cg *scg; 1910113db2ddSJeff Roberson int i; 1911113db2ddSJeff Roberson 1912113db2ddSJeff Roberson for (i = 0; i < SUJ_HASHSIZE; i++) 1913113db2ddSJeff Roberson LIST_FOREACH(scg, &cghash[i], sc_next) 1914113db2ddSJeff Roberson apply(scg); 1915113db2ddSJeff Roberson } 1916113db2ddSJeff Roberson 1917113db2ddSJeff Roberson /* 1918113db2ddSJeff Roberson * Process the unlinked but referenced file list. Freeing all inodes. 1919113db2ddSJeff Roberson */ 1920113db2ddSJeff Roberson static void 1921113db2ddSJeff Roberson ino_unlinked(void) 1922113db2ddSJeff Roberson { 1923113db2ddSJeff Roberson union dinode *ip; 1924113db2ddSJeff Roberson uint16_t mode; 1925113db2ddSJeff Roberson ino_t inon; 1926113db2ddSJeff Roberson ino_t ino; 1927113db2ddSJeff Roberson 1928113db2ddSJeff Roberson ino = fs->fs_sujfree; 1929113db2ddSJeff Roberson fs->fs_sujfree = 0; 1930113db2ddSJeff Roberson while (ino != 0) { 1931113db2ddSJeff Roberson ip = ino_read(ino); 1932113db2ddSJeff Roberson mode = DIP(ip, di_mode) & IFMT; 1933113db2ddSJeff Roberson inon = DIP(ip, di_freelink); 1934113db2ddSJeff Roberson DIP_SET(ip, di_freelink, 0); 1935113db2ddSJeff Roberson /* 1936113db2ddSJeff Roberson * XXX Should this be an errx? 1937113db2ddSJeff Roberson */ 1938113db2ddSJeff Roberson if (DIP(ip, di_nlink) == 0) { 1939113db2ddSJeff Roberson if (debug) 1940623d7cb6SMatthew D Fleming printf("Freeing unlinked ino %ju mode %o\n", 1941e25a029eSMatthew D Fleming (uintmax_t)ino, mode); 1942113db2ddSJeff Roberson ino_reclaim(ip, ino, mode); 1943113db2ddSJeff Roberson } else if (debug) 1944623d7cb6SMatthew D Fleming printf("Skipping ino %ju mode %o with link %d\n", 1945623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, DIP(ip, di_nlink)); 1946113db2ddSJeff Roberson ino = inon; 1947113db2ddSJeff Roberson } 1948113db2ddSJeff Roberson } 1949113db2ddSJeff Roberson 1950113db2ddSJeff Roberson /* 1951113db2ddSJeff Roberson * Append a new record to the list of records requiring processing. 1952113db2ddSJeff Roberson */ 1953113db2ddSJeff Roberson static void 1954113db2ddSJeff Roberson ino_append(union jrec *rec) 1955113db2ddSJeff Roberson { 1956113db2ddSJeff Roberson struct jrefrec *refrec; 1957113db2ddSJeff Roberson struct jmvrec *mvrec; 1958113db2ddSJeff Roberson struct suj_ino *sino; 1959113db2ddSJeff Roberson struct suj_rec *srec; 1960113db2ddSJeff Roberson 1961113db2ddSJeff Roberson mvrec = &rec->rec_jmvrec; 1962113db2ddSJeff Roberson refrec = &rec->rec_jrefrec; 1963113db2ddSJeff Roberson if (debug && mvrec->jm_op == JOP_MVREF) 1964113db2ddSJeff Roberson printf("ino move: ino %d, parent %d, diroff %jd, oldoff %jd\n", 1965113db2ddSJeff Roberson mvrec->jm_ino, mvrec->jm_parent, mvrec->jm_newoff, 1966113db2ddSJeff Roberson mvrec->jm_oldoff); 1967113db2ddSJeff Roberson else if (debug && 1968113db2ddSJeff Roberson (refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF)) 1969113db2ddSJeff Roberson printf("ino ref: op %d, ino %d, nlink %d, " 1970113db2ddSJeff Roberson "parent %d, diroff %jd\n", 1971113db2ddSJeff Roberson refrec->jr_op, refrec->jr_ino, refrec->jr_nlink, 1972113db2ddSJeff Roberson refrec->jr_parent, refrec->jr_diroff); 1973113db2ddSJeff Roberson sino = ino_lookup(((struct jrefrec *)rec)->jr_ino, 1); 1974113db2ddSJeff Roberson sino->si_hasrecs = 1; 1975113db2ddSJeff Roberson srec = errmalloc(sizeof(*srec)); 1976113db2ddSJeff Roberson srec->sr_rec = rec; 1977113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_newrecs, srec, sr_next); 1978113db2ddSJeff Roberson } 1979113db2ddSJeff Roberson 1980113db2ddSJeff Roberson /* 1981113db2ddSJeff Roberson * Add a reference adjustment to the sino list and eliminate dups. The 1982113db2ddSJeff Roberson * primary loop in ino_build_ref() checks for dups but new ones may be 1983113db2ddSJeff Roberson * created as a result of offset adjustments. 1984113db2ddSJeff Roberson */ 1985113db2ddSJeff Roberson static void 1986113db2ddSJeff Roberson ino_add_ref(struct suj_ino *sino, struct suj_rec *srec) 1987113db2ddSJeff Roberson { 1988113db2ddSJeff Roberson struct jrefrec *refrec; 1989113db2ddSJeff Roberson struct suj_rec *srn; 1990113db2ddSJeff Roberson struct jrefrec *rrn; 1991113db2ddSJeff Roberson 1992113db2ddSJeff Roberson refrec = (struct jrefrec *)srec->sr_rec; 1993113db2ddSJeff Roberson /* 1994113db2ddSJeff Roberson * We walk backwards so that the oldest link count is preserved. If 1995113db2ddSJeff Roberson * an add record conflicts with a remove keep the remove. Redundant 1996113db2ddSJeff Roberson * removes are eliminated in ino_build_ref. Otherwise we keep the 1997113db2ddSJeff Roberson * oldest record at a given location. 1998113db2ddSJeff Roberson */ 1999113db2ddSJeff Roberson for (srn = TAILQ_LAST(&sino->si_recs, srechd); srn; 2000113db2ddSJeff Roberson srn = TAILQ_PREV(srn, srechd, sr_next)) { 2001113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 2002113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 2003113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 2004113db2ddSJeff Roberson continue; 2005113db2ddSJeff Roberson if (rrn->jr_op == JOP_REMREF || refrec->jr_op == JOP_ADDREF) { 2006113db2ddSJeff Roberson rrn->jr_mode = refrec->jr_mode; 2007113db2ddSJeff Roberson return; 2008113db2ddSJeff Roberson } 2009113db2ddSJeff Roberson /* 2010113db2ddSJeff Roberson * Adding a remove. 2011113db2ddSJeff Roberson * 2012113db2ddSJeff Roberson * Replace the record in place with the old nlink in case 2013113db2ddSJeff Roberson * we replace the head of the list. Abandon srec as a dup. 2014113db2ddSJeff Roberson */ 2015113db2ddSJeff Roberson refrec->jr_nlink = rrn->jr_nlink; 2016113db2ddSJeff Roberson srn->sr_rec = srec->sr_rec; 2017113db2ddSJeff Roberson return; 2018113db2ddSJeff Roberson } 2019113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_recs, srec, sr_next); 2020113db2ddSJeff Roberson } 2021113db2ddSJeff Roberson 2022113db2ddSJeff Roberson /* 2023113db2ddSJeff Roberson * Create a duplicate of a reference at a previous location. 2024113db2ddSJeff Roberson */ 2025113db2ddSJeff Roberson static void 2026113db2ddSJeff Roberson ino_dup_ref(struct suj_ino *sino, struct jrefrec *refrec, off_t diroff) 2027113db2ddSJeff Roberson { 2028113db2ddSJeff Roberson struct jrefrec *rrn; 2029113db2ddSJeff Roberson struct suj_rec *srn; 2030113db2ddSJeff Roberson 2031113db2ddSJeff Roberson rrn = errmalloc(sizeof(*refrec)); 2032113db2ddSJeff Roberson *rrn = *refrec; 2033113db2ddSJeff Roberson rrn->jr_op = JOP_ADDREF; 2034113db2ddSJeff Roberson rrn->jr_diroff = diroff; 2035113db2ddSJeff Roberson srn = errmalloc(sizeof(*srn)); 2036113db2ddSJeff Roberson srn->sr_rec = (union jrec *)rrn; 2037113db2ddSJeff Roberson ino_add_ref(sino, srn); 2038113db2ddSJeff Roberson } 2039113db2ddSJeff Roberson 2040113db2ddSJeff Roberson /* 2041113db2ddSJeff Roberson * Add a reference to the list at all known locations. We follow the offset 2042113db2ddSJeff Roberson * changes for a single instance and create duplicate add refs at each so 2043113db2ddSJeff Roberson * that we can tolerate any version of the directory block. Eliminate 2044113db2ddSJeff Roberson * removes which collide with adds that are seen in the journal. They should 2045113db2ddSJeff Roberson * not adjust the link count down. 2046113db2ddSJeff Roberson */ 2047113db2ddSJeff Roberson static void 2048113db2ddSJeff Roberson ino_build_ref(struct suj_ino *sino, struct suj_rec *srec) 2049113db2ddSJeff Roberson { 2050113db2ddSJeff Roberson struct jrefrec *refrec; 2051113db2ddSJeff Roberson struct jmvrec *mvrec; 2052113db2ddSJeff Roberson struct suj_rec *srp; 2053113db2ddSJeff Roberson struct suj_rec *srn; 2054113db2ddSJeff Roberson struct jrefrec *rrn; 2055113db2ddSJeff Roberson off_t diroff; 2056113db2ddSJeff Roberson 2057113db2ddSJeff Roberson refrec = (struct jrefrec *)srec->sr_rec; 2058113db2ddSJeff Roberson /* 2059113db2ddSJeff Roberson * Search for a mvrec that matches this offset. Whether it's an add 2060113db2ddSJeff Roberson * or a remove we can delete the mvref after creating a dup record in 2061113db2ddSJeff Roberson * the old location. 2062113db2ddSJeff Roberson */ 2063113db2ddSJeff Roberson if (!TAILQ_EMPTY(&sino->si_movs)) { 2064113db2ddSJeff Roberson diroff = refrec->jr_diroff; 2065113db2ddSJeff Roberson for (srn = TAILQ_LAST(&sino->si_movs, srechd); srn; srn = srp) { 2066113db2ddSJeff Roberson srp = TAILQ_PREV(srn, srechd, sr_next); 2067113db2ddSJeff Roberson mvrec = (struct jmvrec *)srn->sr_rec; 2068113db2ddSJeff Roberson if (mvrec->jm_parent != refrec->jr_parent || 2069113db2ddSJeff Roberson mvrec->jm_newoff != diroff) 2070113db2ddSJeff Roberson continue; 2071113db2ddSJeff Roberson diroff = mvrec->jm_oldoff; 2072113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_movs, srn, sr_next); 2073edad6026SXin LI free(srn); 2074113db2ddSJeff Roberson ino_dup_ref(sino, refrec, diroff); 2075113db2ddSJeff Roberson } 2076113db2ddSJeff Roberson } 2077113db2ddSJeff Roberson /* 2078113db2ddSJeff Roberson * If a remove wasn't eliminated by an earlier add just append it to 2079113db2ddSJeff Roberson * the list. 2080113db2ddSJeff Roberson */ 2081113db2ddSJeff Roberson if (refrec->jr_op == JOP_REMREF) { 2082113db2ddSJeff Roberson ino_add_ref(sino, srec); 2083113db2ddSJeff Roberson return; 2084113db2ddSJeff Roberson } 2085113db2ddSJeff Roberson /* 2086113db2ddSJeff Roberson * Walk the list of records waiting to be added to the list. We 2087113db2ddSJeff Roberson * must check for moves that apply to our current offset and remove 2088113db2ddSJeff Roberson * them from the list. Remove any duplicates to eliminate removes 2089113db2ddSJeff Roberson * with corresponding adds. 2090113db2ddSJeff Roberson */ 2091113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(srn, &sino->si_newrecs, sr_next, srp) { 2092113db2ddSJeff Roberson switch (srn->sr_rec->rec_jrefrec.jr_op) { 2093113db2ddSJeff Roberson case JOP_ADDREF: 2094113db2ddSJeff Roberson /* 2095113db2ddSJeff Roberson * This should actually be an error we should 2096113db2ddSJeff Roberson * have a remove for every add journaled. 2097113db2ddSJeff Roberson */ 2098113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 2099113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 2100113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 2101113db2ddSJeff Roberson break; 2102113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 2103113db2ddSJeff Roberson break; 2104113db2ddSJeff Roberson case JOP_REMREF: 2105113db2ddSJeff Roberson /* 2106113db2ddSJeff Roberson * Once we remove the current iteration of the 2107113db2ddSJeff Roberson * record at this address we're done. 2108113db2ddSJeff Roberson */ 2109113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 2110113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 2111113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 2112113db2ddSJeff Roberson break; 2113113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 2114113db2ddSJeff Roberson ino_add_ref(sino, srec); 2115113db2ddSJeff Roberson return; 2116113db2ddSJeff Roberson case JOP_MVREF: 2117113db2ddSJeff Roberson /* 2118113db2ddSJeff Roberson * Update our diroff based on any moves that match 2119113db2ddSJeff Roberson * and remove the move. 2120113db2ddSJeff Roberson */ 2121113db2ddSJeff Roberson mvrec = (struct jmvrec *)srn->sr_rec; 2122113db2ddSJeff Roberson if (mvrec->jm_parent != refrec->jr_parent || 2123113db2ddSJeff Roberson mvrec->jm_oldoff != refrec->jr_diroff) 2124113db2ddSJeff Roberson break; 2125113db2ddSJeff Roberson ino_dup_ref(sino, refrec, mvrec->jm_oldoff); 2126113db2ddSJeff Roberson refrec->jr_diroff = mvrec->jm_newoff; 2127113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 2128113db2ddSJeff Roberson break; 2129113db2ddSJeff Roberson default: 2130edad6026SXin LI err_suj("ino_build_ref: Unknown op %d\n", 2131113db2ddSJeff Roberson srn->sr_rec->rec_jrefrec.jr_op); 2132113db2ddSJeff Roberson } 2133113db2ddSJeff Roberson } 2134113db2ddSJeff Roberson ino_add_ref(sino, srec); 2135113db2ddSJeff Roberson } 2136113db2ddSJeff Roberson 2137113db2ddSJeff Roberson /* 2138113db2ddSJeff Roberson * Walk the list of new records and add them in-order resolving any 2139113db2ddSJeff Roberson * dups and adjusted offsets. 2140113db2ddSJeff Roberson */ 2141113db2ddSJeff Roberson static void 2142113db2ddSJeff Roberson ino_build(struct suj_ino *sino) 2143113db2ddSJeff Roberson { 2144113db2ddSJeff Roberson struct suj_rec *srec; 2145113db2ddSJeff Roberson 2146113db2ddSJeff Roberson while ((srec = TAILQ_FIRST(&sino->si_newrecs)) != NULL) { 2147113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srec, sr_next); 2148113db2ddSJeff Roberson switch (srec->sr_rec->rec_jrefrec.jr_op) { 2149113db2ddSJeff Roberson case JOP_ADDREF: 2150113db2ddSJeff Roberson case JOP_REMREF: 2151113db2ddSJeff Roberson ino_build_ref(sino, srec); 2152113db2ddSJeff Roberson break; 2153113db2ddSJeff Roberson case JOP_MVREF: 2154113db2ddSJeff Roberson /* 2155113db2ddSJeff Roberson * Add this mvrec to the queue of pending mvs. 2156113db2ddSJeff Roberson */ 2157113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next); 2158113db2ddSJeff Roberson break; 2159113db2ddSJeff Roberson default: 2160edad6026SXin LI err_suj("ino_build: Unknown op %d\n", 2161113db2ddSJeff Roberson srec->sr_rec->rec_jrefrec.jr_op); 2162113db2ddSJeff Roberson } 2163113db2ddSJeff Roberson } 2164113db2ddSJeff Roberson if (TAILQ_EMPTY(&sino->si_recs)) 2165113db2ddSJeff Roberson sino->si_hasrecs = 0; 2166113db2ddSJeff Roberson } 2167113db2ddSJeff Roberson 2168113db2ddSJeff Roberson /* 2169113db2ddSJeff Roberson * Modify journal records so they refer to the base block number 2170113db2ddSJeff Roberson * and a start and end frag range. This is to facilitate the discovery 2171113db2ddSJeff Roberson * of overlapping fragment allocations. 2172113db2ddSJeff Roberson */ 2173113db2ddSJeff Roberson static void 2174113db2ddSJeff Roberson blk_build(struct jblkrec *blkrec) 2175113db2ddSJeff Roberson { 2176113db2ddSJeff Roberson struct suj_rec *srec; 2177113db2ddSJeff Roberson struct suj_blk *sblk; 2178113db2ddSJeff Roberson struct jblkrec *blkrn; 2179113db2ddSJeff Roberson ufs2_daddr_t blk; 2180113db2ddSJeff Roberson int frag; 2181113db2ddSJeff Roberson 2182113db2ddSJeff Roberson if (debug) 2183113db2ddSJeff Roberson printf("blk_build: op %d blkno %jd frags %d oldfrags %d " 2184113db2ddSJeff Roberson "ino %d lbn %jd\n", 2185113db2ddSJeff Roberson blkrec->jb_op, blkrec->jb_blkno, blkrec->jb_frags, 2186113db2ddSJeff Roberson blkrec->jb_oldfrags, blkrec->jb_ino, blkrec->jb_lbn); 2187113db2ddSJeff Roberson 2188113db2ddSJeff Roberson blk = blknum(fs, blkrec->jb_blkno); 2189113db2ddSJeff Roberson frag = fragnum(fs, blkrec->jb_blkno); 2190113db2ddSJeff Roberson sblk = blk_lookup(blk, 1); 2191113db2ddSJeff Roberson /* 2192113db2ddSJeff Roberson * Rewrite the record using oldfrags to indicate the offset into 2193113db2ddSJeff Roberson * the block. Leave jb_frags as the actual allocated count. 2194113db2ddSJeff Roberson */ 2195113db2ddSJeff Roberson blkrec->jb_blkno -= frag; 2196113db2ddSJeff Roberson blkrec->jb_oldfrags = frag; 2197113db2ddSJeff Roberson if (blkrec->jb_oldfrags + blkrec->jb_frags > fs->fs_frag) 2198edad6026SXin LI err_suj("Invalid fragment count %d oldfrags %d\n", 2199113db2ddSJeff Roberson blkrec->jb_frags, frag); 2200113db2ddSJeff Roberson /* 2201113db2ddSJeff Roberson * Detect dups. If we detect a dup we always discard the oldest 2202113db2ddSJeff Roberson * record as it is superseded by the new record. This speeds up 2203113db2ddSJeff Roberson * later stages but also eliminates free records which are used 2204113db2ddSJeff Roberson * to indicate that the contents of indirects can be trusted. 2205113db2ddSJeff Roberson */ 2206113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 2207113db2ddSJeff Roberson blkrn = (struct jblkrec *)srec->sr_rec; 2208113db2ddSJeff Roberson if (blkrn->jb_ino != blkrec->jb_ino || 2209113db2ddSJeff Roberson blkrn->jb_lbn != blkrec->jb_lbn || 2210113db2ddSJeff Roberson blkrn->jb_blkno != blkrec->jb_blkno || 2211113db2ddSJeff Roberson blkrn->jb_frags != blkrec->jb_frags || 2212113db2ddSJeff Roberson blkrn->jb_oldfrags != blkrec->jb_oldfrags) 2213113db2ddSJeff Roberson continue; 2214113db2ddSJeff Roberson if (debug) 2215113db2ddSJeff Roberson printf("Removed dup.\n"); 2216113db2ddSJeff Roberson /* Discard the free which is a dup with an alloc. */ 2217113db2ddSJeff Roberson if (blkrec->jb_op == JOP_FREEBLK) 2218113db2ddSJeff Roberson return; 2219113db2ddSJeff Roberson TAILQ_REMOVE(&sblk->sb_recs, srec, sr_next); 2220113db2ddSJeff Roberson free(srec); 2221113db2ddSJeff Roberson break; 2222113db2ddSJeff Roberson } 2223113db2ddSJeff Roberson srec = errmalloc(sizeof(*srec)); 2224113db2ddSJeff Roberson srec->sr_rec = (union jrec *)blkrec; 2225113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sblk->sb_recs, srec, sr_next); 2226113db2ddSJeff Roberson } 2227113db2ddSJeff Roberson 2228113db2ddSJeff Roberson static void 2229113db2ddSJeff Roberson ino_build_trunc(struct jtrncrec *rec) 2230113db2ddSJeff Roberson { 2231113db2ddSJeff Roberson struct suj_ino *sino; 2232113db2ddSJeff Roberson 2233113db2ddSJeff Roberson if (debug) 2234280e091aSJeff Roberson printf("ino_build_trunc: op %d ino %d, size %jd\n", 2235280e091aSJeff Roberson rec->jt_op, rec->jt_ino, rec->jt_size); 2236113db2ddSJeff Roberson sino = ino_lookup(rec->jt_ino, 1); 2237280e091aSJeff Roberson if (rec->jt_op == JOP_SYNC) { 2238280e091aSJeff Roberson sino->si_trunc = NULL; 2239280e091aSJeff Roberson return; 2240280e091aSJeff Roberson } 2241280e091aSJeff Roberson if (sino->si_trunc == NULL || sino->si_trunc->jt_size > rec->jt_size) 2242113db2ddSJeff Roberson sino->si_trunc = rec; 2243113db2ddSJeff Roberson } 2244113db2ddSJeff Roberson 2245113db2ddSJeff Roberson /* 2246113db2ddSJeff Roberson * Build up tables of the operations we need to recover. 2247113db2ddSJeff Roberson */ 2248113db2ddSJeff Roberson static void 2249113db2ddSJeff Roberson suj_build(void) 2250113db2ddSJeff Roberson { 2251113db2ddSJeff Roberson struct suj_seg *seg; 2252113db2ddSJeff Roberson union jrec *rec; 2253113db2ddSJeff Roberson int off; 2254113db2ddSJeff Roberson int i; 2255113db2ddSJeff Roberson 2256113db2ddSJeff Roberson TAILQ_FOREACH(seg, &allsegs, ss_next) { 2257113db2ddSJeff Roberson if (debug) 2258113db2ddSJeff Roberson printf("seg %jd has %d records, oldseq %jd.\n", 2259113db2ddSJeff Roberson seg->ss_rec.jsr_seq, seg->ss_rec.jsr_cnt, 2260113db2ddSJeff Roberson seg->ss_rec.jsr_oldest); 2261113db2ddSJeff Roberson off = 0; 2262113db2ddSJeff Roberson rec = (union jrec *)seg->ss_blk; 2263113db2ddSJeff Roberson for (i = 0; i < seg->ss_rec.jsr_cnt; off += JREC_SIZE, rec++) { 2264113db2ddSJeff Roberson /* skip the segrec. */ 22650947d19aSKonstantin Belousov if ((off % real_dev_bsize) == 0) 2266113db2ddSJeff Roberson continue; 2267113db2ddSJeff Roberson switch (rec->rec_jrefrec.jr_op) { 2268113db2ddSJeff Roberson case JOP_ADDREF: 2269113db2ddSJeff Roberson case JOP_REMREF: 2270113db2ddSJeff Roberson case JOP_MVREF: 2271113db2ddSJeff Roberson ino_append(rec); 2272113db2ddSJeff Roberson break; 2273113db2ddSJeff Roberson case JOP_NEWBLK: 2274113db2ddSJeff Roberson case JOP_FREEBLK: 2275113db2ddSJeff Roberson blk_build((struct jblkrec *)rec); 2276113db2ddSJeff Roberson break; 2277113db2ddSJeff Roberson case JOP_TRUNC: 227885e9da38SJeff Roberson case JOP_SYNC: 2279113db2ddSJeff Roberson ino_build_trunc((struct jtrncrec *)rec); 2280113db2ddSJeff Roberson break; 2281113db2ddSJeff Roberson default: 2282edad6026SXin LI err_suj("Unknown journal operation %d (%d)\n", 2283113db2ddSJeff Roberson rec->rec_jrefrec.jr_op, off); 2284113db2ddSJeff Roberson } 2285113db2ddSJeff Roberson i++; 2286113db2ddSJeff Roberson } 2287113db2ddSJeff Roberson } 2288113db2ddSJeff Roberson } 2289113db2ddSJeff Roberson 2290113db2ddSJeff Roberson /* 2291113db2ddSJeff Roberson * Prune the journal segments to those we care about based on the 2292113db2ddSJeff Roberson * oldest sequence in the newest segment. Order the segment list 2293113db2ddSJeff Roberson * based on sequence number. 2294113db2ddSJeff Roberson */ 2295113db2ddSJeff Roberson static void 2296113db2ddSJeff Roberson suj_prune(void) 2297113db2ddSJeff Roberson { 2298113db2ddSJeff Roberson struct suj_seg *seg; 2299113db2ddSJeff Roberson struct suj_seg *segn; 2300113db2ddSJeff Roberson uint64_t newseq; 2301113db2ddSJeff Roberson int discard; 2302113db2ddSJeff Roberson 2303113db2ddSJeff Roberson if (debug) 2304113db2ddSJeff Roberson printf("Pruning up to %jd\n", oldseq); 2305113db2ddSJeff Roberson /* First free the expired segments. */ 2306113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2307113db2ddSJeff Roberson if (seg->ss_rec.jsr_seq >= oldseq) 2308113db2ddSJeff Roberson continue; 2309113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2310113db2ddSJeff Roberson free(seg->ss_blk); 2311113db2ddSJeff Roberson free(seg); 2312113db2ddSJeff Roberson } 2313113db2ddSJeff Roberson /* Next ensure that segments are ordered properly. */ 2314113db2ddSJeff Roberson seg = TAILQ_FIRST(&allsegs); 2315113db2ddSJeff Roberson if (seg == NULL) { 2316113db2ddSJeff Roberson if (debug) 2317113db2ddSJeff Roberson printf("Empty journal\n"); 2318113db2ddSJeff Roberson return; 2319113db2ddSJeff Roberson } 2320113db2ddSJeff Roberson newseq = seg->ss_rec.jsr_seq; 2321113db2ddSJeff Roberson for (;;) { 2322113db2ddSJeff Roberson seg = TAILQ_LAST(&allsegs, seghd); 2323113db2ddSJeff Roberson if (seg->ss_rec.jsr_seq >= newseq) 2324113db2ddSJeff Roberson break; 2325113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2326113db2ddSJeff Roberson TAILQ_INSERT_HEAD(&allsegs, seg, ss_next); 2327113db2ddSJeff Roberson newseq = seg->ss_rec.jsr_seq; 2328113db2ddSJeff Roberson 2329113db2ddSJeff Roberson } 2330edad6026SXin LI if (newseq != oldseq) { 23312db62a6bSJeff Roberson TAILQ_FOREACH(seg, &allsegs, ss_next) { 23322db62a6bSJeff Roberson printf("%jd, ", seg->ss_rec.jsr_seq); 23332db62a6bSJeff Roberson } 23342db62a6bSJeff Roberson printf("\n"); 2335edad6026SXin LI err_suj("Journal file sequence mismatch %jd != %jd\n", 2336113db2ddSJeff Roberson newseq, oldseq); 2337edad6026SXin LI } 2338113db2ddSJeff Roberson /* 2339113db2ddSJeff Roberson * The kernel may asynchronously write segments which can create 2340113db2ddSJeff Roberson * gaps in the sequence space. Throw away any segments after the 2341113db2ddSJeff Roberson * gap as the kernel guarantees only those that are contiguously 2342113db2ddSJeff Roberson * reachable are marked as completed. 2343113db2ddSJeff Roberson */ 2344113db2ddSJeff Roberson discard = 0; 2345113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2346113db2ddSJeff Roberson if (!discard && newseq++ == seg->ss_rec.jsr_seq) { 2347113db2ddSJeff Roberson jrecs += seg->ss_rec.jsr_cnt; 23480947d19aSKonstantin Belousov jbytes += seg->ss_rec.jsr_blocks * real_dev_bsize; 2349113db2ddSJeff Roberson continue; 2350113db2ddSJeff Roberson } 2351113db2ddSJeff Roberson discard = 1; 2352113db2ddSJeff Roberson if (debug) 2353113db2ddSJeff Roberson printf("Journal order mismatch %jd != %jd pruning\n", 2354113db2ddSJeff Roberson newseq-1, seg->ss_rec.jsr_seq); 2355113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2356113db2ddSJeff Roberson free(seg->ss_blk); 2357113db2ddSJeff Roberson free(seg); 2358113db2ddSJeff Roberson } 2359113db2ddSJeff Roberson if (debug) 2360113db2ddSJeff Roberson printf("Processing journal segments from %jd to %jd\n", 2361113db2ddSJeff Roberson oldseq, newseq-1); 2362113db2ddSJeff Roberson } 2363113db2ddSJeff Roberson 2364113db2ddSJeff Roberson /* 2365113db2ddSJeff Roberson * Verify the journal inode before attempting to read records. 2366113db2ddSJeff Roberson */ 2367113db2ddSJeff Roberson static int 2368113db2ddSJeff Roberson suj_verifyino(union dinode *ip) 2369113db2ddSJeff Roberson { 2370113db2ddSJeff Roberson 2371113db2ddSJeff Roberson if (DIP(ip, di_nlink) != 1) { 2372623d7cb6SMatthew D Fleming printf("Invalid link count %d for journal inode %ju\n", 2373623d7cb6SMatthew D Fleming DIP(ip, di_nlink), (uintmax_t)sujino); 2374113db2ddSJeff Roberson return (-1); 2375113db2ddSJeff Roberson } 2376113db2ddSJeff Roberson 2377113db2ddSJeff Roberson if ((DIP(ip, di_flags) & (SF_IMMUTABLE | SF_NOUNLINK)) != 2378113db2ddSJeff Roberson (SF_IMMUTABLE | SF_NOUNLINK)) { 2379623d7cb6SMatthew D Fleming printf("Invalid flags 0x%X for journal inode %ju\n", 2380623d7cb6SMatthew D Fleming DIP(ip, di_flags), (uintmax_t)sujino); 2381113db2ddSJeff Roberson return (-1); 2382113db2ddSJeff Roberson } 2383113db2ddSJeff Roberson 2384113db2ddSJeff Roberson if (DIP(ip, di_mode) != (IFREG | IREAD)) { 2385623d7cb6SMatthew D Fleming printf("Invalid mode %o for journal inode %ju\n", 2386623d7cb6SMatthew D Fleming DIP(ip, di_mode), (uintmax_t)sujino); 2387113db2ddSJeff Roberson return (-1); 2388113db2ddSJeff Roberson } 2389113db2ddSJeff Roberson 23902db8baa9SKonstantin Belousov if (DIP(ip, di_size) < SUJ_MIN) { 2391623d7cb6SMatthew D Fleming printf("Invalid size %jd for journal inode %ju\n", 2392623d7cb6SMatthew D Fleming DIP(ip, di_size), (uintmax_t)sujino); 2393113db2ddSJeff Roberson return (-1); 2394113db2ddSJeff Roberson } 2395113db2ddSJeff Roberson 2396113db2ddSJeff Roberson if (DIP(ip, di_modrev) != fs->fs_mtime) { 2397113db2ddSJeff Roberson printf("Journal timestamp does not match fs mount time\n"); 2398113db2ddSJeff Roberson return (-1); 2399113db2ddSJeff Roberson } 2400113db2ddSJeff Roberson 2401113db2ddSJeff Roberson return (0); 2402113db2ddSJeff Roberson } 2403113db2ddSJeff Roberson 2404113db2ddSJeff Roberson struct jblocks { 2405113db2ddSJeff Roberson struct jextent *jb_extent; /* Extent array. */ 2406113db2ddSJeff Roberson int jb_avail; /* Available extents. */ 2407113db2ddSJeff Roberson int jb_used; /* Last used extent. */ 2408113db2ddSJeff Roberson int jb_head; /* Allocator head. */ 2409113db2ddSJeff Roberson int jb_off; /* Allocator extent offset. */ 2410113db2ddSJeff Roberson }; 2411113db2ddSJeff Roberson struct jextent { 2412113db2ddSJeff Roberson ufs2_daddr_t je_daddr; /* Disk block address. */ 2413113db2ddSJeff Roberson int je_blocks; /* Disk block count. */ 2414113db2ddSJeff Roberson }; 2415113db2ddSJeff Roberson 2416113db2ddSJeff Roberson struct jblocks *suj_jblocks; 2417113db2ddSJeff Roberson 2418113db2ddSJeff Roberson static struct jblocks * 2419113db2ddSJeff Roberson jblocks_create(void) 2420113db2ddSJeff Roberson { 2421113db2ddSJeff Roberson struct jblocks *jblocks; 2422113db2ddSJeff Roberson int size; 2423113db2ddSJeff Roberson 2424113db2ddSJeff Roberson jblocks = errmalloc(sizeof(*jblocks)); 2425113db2ddSJeff Roberson jblocks->jb_avail = 10; 2426113db2ddSJeff Roberson jblocks->jb_used = 0; 2427113db2ddSJeff Roberson jblocks->jb_head = 0; 2428113db2ddSJeff Roberson jblocks->jb_off = 0; 2429113db2ddSJeff Roberson size = sizeof(struct jextent) * jblocks->jb_avail; 2430113db2ddSJeff Roberson jblocks->jb_extent = errmalloc(size); 2431113db2ddSJeff Roberson bzero(jblocks->jb_extent, size); 2432113db2ddSJeff Roberson 2433113db2ddSJeff Roberson return (jblocks); 2434113db2ddSJeff Roberson } 2435113db2ddSJeff Roberson 2436113db2ddSJeff Roberson /* 2437113db2ddSJeff Roberson * Return the next available disk block and the amount of contiguous 2438113db2ddSJeff Roberson * free space it contains. 2439113db2ddSJeff Roberson */ 2440113db2ddSJeff Roberson static ufs2_daddr_t 2441113db2ddSJeff Roberson jblocks_next(struct jblocks *jblocks, int bytes, int *actual) 2442113db2ddSJeff Roberson { 2443113db2ddSJeff Roberson struct jextent *jext; 2444113db2ddSJeff Roberson ufs2_daddr_t daddr; 2445113db2ddSJeff Roberson int freecnt; 2446113db2ddSJeff Roberson int blocks; 2447113db2ddSJeff Roberson 24480947d19aSKonstantin Belousov blocks = bytes / disk->d_bsize; 2449113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_head]; 2450113db2ddSJeff Roberson freecnt = jext->je_blocks - jblocks->jb_off; 2451113db2ddSJeff Roberson if (freecnt == 0) { 2452113db2ddSJeff Roberson jblocks->jb_off = 0; 2453113db2ddSJeff Roberson if (++jblocks->jb_head > jblocks->jb_used) 2454113db2ddSJeff Roberson return (0); 2455113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_head]; 2456113db2ddSJeff Roberson freecnt = jext->je_blocks; 2457113db2ddSJeff Roberson } 2458113db2ddSJeff Roberson if (freecnt > blocks) 2459113db2ddSJeff Roberson freecnt = blocks; 24600947d19aSKonstantin Belousov *actual = freecnt * disk->d_bsize; 2461113db2ddSJeff Roberson daddr = jext->je_daddr + jblocks->jb_off; 2462113db2ddSJeff Roberson 2463113db2ddSJeff Roberson return (daddr); 2464113db2ddSJeff Roberson } 2465113db2ddSJeff Roberson 2466113db2ddSJeff Roberson /* 2467113db2ddSJeff Roberson * Advance the allocation head by a specified number of bytes, consuming 2468113db2ddSJeff Roberson * one journal segment. 2469113db2ddSJeff Roberson */ 2470113db2ddSJeff Roberson static void 2471113db2ddSJeff Roberson jblocks_advance(struct jblocks *jblocks, int bytes) 2472113db2ddSJeff Roberson { 2473113db2ddSJeff Roberson 24740947d19aSKonstantin Belousov jblocks->jb_off += bytes / disk->d_bsize; 2475113db2ddSJeff Roberson } 2476113db2ddSJeff Roberson 2477113db2ddSJeff Roberson static void 2478113db2ddSJeff Roberson jblocks_destroy(struct jblocks *jblocks) 2479113db2ddSJeff Roberson { 2480113db2ddSJeff Roberson 2481113db2ddSJeff Roberson free(jblocks->jb_extent); 2482113db2ddSJeff Roberson free(jblocks); 2483113db2ddSJeff Roberson } 2484113db2ddSJeff Roberson 2485113db2ddSJeff Roberson static void 2486113db2ddSJeff Roberson jblocks_add(struct jblocks *jblocks, ufs2_daddr_t daddr, int blocks) 2487113db2ddSJeff Roberson { 2488113db2ddSJeff Roberson struct jextent *jext; 2489113db2ddSJeff Roberson int size; 2490113db2ddSJeff Roberson 2491113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_used]; 2492113db2ddSJeff Roberson /* Adding the first block. */ 2493113db2ddSJeff Roberson if (jext->je_daddr == 0) { 2494113db2ddSJeff Roberson jext->je_daddr = daddr; 2495113db2ddSJeff Roberson jext->je_blocks = blocks; 2496113db2ddSJeff Roberson return; 2497113db2ddSJeff Roberson } 2498113db2ddSJeff Roberson /* Extending the last extent. */ 2499113db2ddSJeff Roberson if (jext->je_daddr + jext->je_blocks == daddr) { 2500113db2ddSJeff Roberson jext->je_blocks += blocks; 2501113db2ddSJeff Roberson return; 2502113db2ddSJeff Roberson } 2503113db2ddSJeff Roberson /* Adding a new extent. */ 2504113db2ddSJeff Roberson if (++jblocks->jb_used == jblocks->jb_avail) { 2505113db2ddSJeff Roberson jblocks->jb_avail *= 2; 2506113db2ddSJeff Roberson size = sizeof(struct jextent) * jblocks->jb_avail; 2507113db2ddSJeff Roberson jext = errmalloc(size); 2508113db2ddSJeff Roberson bzero(jext, size); 2509113db2ddSJeff Roberson bcopy(jblocks->jb_extent, jext, 2510113db2ddSJeff Roberson sizeof(struct jextent) * jblocks->jb_used); 2511113db2ddSJeff Roberson free(jblocks->jb_extent); 2512113db2ddSJeff Roberson jblocks->jb_extent = jext; 2513113db2ddSJeff Roberson } 2514113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_used]; 2515113db2ddSJeff Roberson jext->je_daddr = daddr; 2516113db2ddSJeff Roberson jext->je_blocks = blocks; 2517113db2ddSJeff Roberson 2518113db2ddSJeff Roberson return; 2519113db2ddSJeff Roberson } 2520113db2ddSJeff Roberson 2521113db2ddSJeff Roberson /* 2522113db2ddSJeff Roberson * Add a file block from the journal to the extent map. We can't read 2523113db2ddSJeff Roberson * each file block individually because the kernel treats it as a circular 2524113db2ddSJeff Roberson * buffer and segments may span mutliple contiguous blocks. 2525113db2ddSJeff Roberson */ 2526113db2ddSJeff Roberson static void 2527113db2ddSJeff Roberson suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 2528113db2ddSJeff Roberson { 2529113db2ddSJeff Roberson 2530113db2ddSJeff Roberson jblocks_add(suj_jblocks, fsbtodb(fs, blk), fsbtodb(fs, frags)); 2531113db2ddSJeff Roberson } 2532113db2ddSJeff Roberson 2533113db2ddSJeff Roberson static void 2534113db2ddSJeff Roberson suj_read(void) 2535113db2ddSJeff Roberson { 2536113db2ddSJeff Roberson uint8_t block[1 * 1024 * 1024]; 2537113db2ddSJeff Roberson struct suj_seg *seg; 2538113db2ddSJeff Roberson struct jsegrec *recn; 2539113db2ddSJeff Roberson struct jsegrec *rec; 2540113db2ddSJeff Roberson ufs2_daddr_t blk; 2541113db2ddSJeff Roberson int readsize; 2542113db2ddSJeff Roberson int blocks; 2543113db2ddSJeff Roberson int recsize; 2544113db2ddSJeff Roberson int size; 2545113db2ddSJeff Roberson int i; 2546113db2ddSJeff Roberson 2547113db2ddSJeff Roberson /* 2548113db2ddSJeff Roberson * Read records until we exhaust the journal space. If we find 2549113db2ddSJeff Roberson * an invalid record we start searching for a valid segment header 2550113db2ddSJeff Roberson * at the next block. This is because we don't have a head/tail 2551113db2ddSJeff Roberson * pointer and must recover the information indirectly. At the gap 2552113db2ddSJeff Roberson * between the head and tail we won't necessarily have a valid 2553113db2ddSJeff Roberson * segment. 2554113db2ddSJeff Roberson */ 2555113db2ddSJeff Roberson restart: 2556113db2ddSJeff Roberson for (;;) { 2557113db2ddSJeff Roberson size = sizeof(block); 2558113db2ddSJeff Roberson blk = jblocks_next(suj_jblocks, size, &readsize); 2559113db2ddSJeff Roberson if (blk == 0) 2560113db2ddSJeff Roberson return; 2561113db2ddSJeff Roberson size = readsize; 2562113db2ddSJeff Roberson /* 2563113db2ddSJeff Roberson * Read 1MB at a time and scan for records within this block. 2564113db2ddSJeff Roberson */ 2565edad6026SXin LI if (bread(disk, blk, &block, size) == -1) { 2566edad6026SXin LI err_suj("Error reading journal block %jd\n", 2567113db2ddSJeff Roberson (intmax_t)blk); 2568edad6026SXin LI } 2569113db2ddSJeff Roberson for (rec = (void *)block; size; size -= recsize, 2570113db2ddSJeff Roberson rec = (struct jsegrec *)((uintptr_t)rec + recsize)) { 25710947d19aSKonstantin Belousov recsize = real_dev_bsize; 2572113db2ddSJeff Roberson if (rec->jsr_time != fs->fs_mtime) { 2573113db2ddSJeff Roberson if (debug) 2574113db2ddSJeff Roberson printf("Rec time %jd != fs mtime %jd\n", 2575113db2ddSJeff Roberson rec->jsr_time, fs->fs_mtime); 2576113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2577113db2ddSJeff Roberson continue; 2578113db2ddSJeff Roberson } 2579113db2ddSJeff Roberson if (rec->jsr_cnt == 0) { 2580113db2ddSJeff Roberson if (debug) 2581113db2ddSJeff Roberson printf("Found illegal count %d\n", 2582113db2ddSJeff Roberson rec->jsr_cnt); 2583113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2584113db2ddSJeff Roberson continue; 2585113db2ddSJeff Roberson } 2586113db2ddSJeff Roberson blocks = rec->jsr_blocks; 25870947d19aSKonstantin Belousov recsize = blocks * real_dev_bsize; 2588113db2ddSJeff Roberson if (recsize > size) { 2589113db2ddSJeff Roberson /* 2590113db2ddSJeff Roberson * We may just have run out of buffer, restart 2591113db2ddSJeff Roberson * the loop to re-read from this spot. 2592113db2ddSJeff Roberson */ 2593113db2ddSJeff Roberson if (size < fs->fs_bsize && 2594113db2ddSJeff Roberson size != readsize && 2595113db2ddSJeff Roberson recsize <= fs->fs_bsize) 2596113db2ddSJeff Roberson goto restart; 2597113db2ddSJeff Roberson if (debug) 2598113db2ddSJeff Roberson printf("Found invalid segsize %d > %d\n", 2599113db2ddSJeff Roberson recsize, size); 26000947d19aSKonstantin Belousov recsize = real_dev_bsize; 2601113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2602113db2ddSJeff Roberson continue; 2603113db2ddSJeff Roberson } 2604113db2ddSJeff Roberson /* 2605113db2ddSJeff Roberson * Verify that all blocks in the segment are present. 2606113db2ddSJeff Roberson */ 2607113db2ddSJeff Roberson for (i = 1; i < blocks; i++) { 26080947d19aSKonstantin Belousov recn = (void *)((uintptr_t)rec) + i * 26090947d19aSKonstantin Belousov real_dev_bsize; 2610113db2ddSJeff Roberson if (recn->jsr_seq == rec->jsr_seq && 2611113db2ddSJeff Roberson recn->jsr_time == rec->jsr_time) 2612113db2ddSJeff Roberson continue; 2613113db2ddSJeff Roberson if (debug) 2614113db2ddSJeff Roberson printf("Incomplete record %jd (%d)\n", 2615113db2ddSJeff Roberson rec->jsr_seq, i); 26160947d19aSKonstantin Belousov recsize = i * real_dev_bsize; 2617113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2618113db2ddSJeff Roberson goto restart; 2619113db2ddSJeff Roberson } 2620113db2ddSJeff Roberson seg = errmalloc(sizeof(*seg)); 2621113db2ddSJeff Roberson seg->ss_blk = errmalloc(recsize); 2622113db2ddSJeff Roberson seg->ss_rec = *rec; 2623113db2ddSJeff Roberson bcopy((void *)rec, seg->ss_blk, recsize); 2624113db2ddSJeff Roberson if (rec->jsr_oldest > oldseq) 2625113db2ddSJeff Roberson oldseq = rec->jsr_oldest; 2626113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&allsegs, seg, ss_next); 2627113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2628113db2ddSJeff Roberson } 2629113db2ddSJeff Roberson } 2630113db2ddSJeff Roberson } 2631113db2ddSJeff Roberson 2632113db2ddSJeff Roberson /* 2633113db2ddSJeff Roberson * Search a directory block for the SUJ_FILE. 2634113db2ddSJeff Roberson */ 2635113db2ddSJeff Roberson static void 2636113db2ddSJeff Roberson suj_find(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 2637113db2ddSJeff Roberson { 2638113db2ddSJeff Roberson char block[MAXBSIZE]; 2639113db2ddSJeff Roberson struct direct *dp; 2640113db2ddSJeff Roberson int bytes; 2641113db2ddSJeff Roberson int off; 2642113db2ddSJeff Roberson 2643113db2ddSJeff Roberson if (sujino) 2644113db2ddSJeff Roberson return; 2645113db2ddSJeff Roberson bytes = lfragtosize(fs, frags); 2646113db2ddSJeff Roberson if (bread(disk, fsbtodb(fs, blk), block, bytes) <= 0) 2647edad6026SXin LI err_suj("Failed to read ROOTINO directory block %jd\n", blk); 2648113db2ddSJeff Roberson for (off = 0; off < bytes; off += dp->d_reclen) { 2649113db2ddSJeff Roberson dp = (struct direct *)&block[off]; 2650113db2ddSJeff Roberson if (dp->d_reclen == 0) 2651113db2ddSJeff Roberson break; 2652113db2ddSJeff Roberson if (dp->d_ino == 0) 2653113db2ddSJeff Roberson continue; 2654113db2ddSJeff Roberson if (dp->d_namlen != strlen(SUJ_FILE)) 2655113db2ddSJeff Roberson continue; 2656113db2ddSJeff Roberson if (bcmp(dp->d_name, SUJ_FILE, dp->d_namlen) != 0) 2657113db2ddSJeff Roberson continue; 2658113db2ddSJeff Roberson sujino = dp->d_ino; 2659113db2ddSJeff Roberson return; 2660113db2ddSJeff Roberson } 2661113db2ddSJeff Roberson } 2662113db2ddSJeff Roberson 2663113db2ddSJeff Roberson /* 2664113db2ddSJeff Roberson * Orchestrate the verification of a filesystem via the softupdates journal. 2665113db2ddSJeff Roberson */ 2666113db2ddSJeff Roberson int 2667113db2ddSJeff Roberson suj_check(const char *filesys) 2668113db2ddSJeff Roberson { 2669113db2ddSJeff Roberson union dinode *jip; 2670113db2ddSJeff Roberson union dinode *ip; 2671113db2ddSJeff Roberson uint64_t blocks; 2672edad6026SXin LI int retval; 2673edad6026SXin LI struct suj_seg *seg; 2674edad6026SXin LI struct suj_seg *segn; 2675113db2ddSJeff Roberson 2676113db2ddSJeff Roberson opendisk(filesys); 2677113db2ddSJeff Roberson TAILQ_INIT(&allsegs); 2678edad6026SXin LI 2679edad6026SXin LI /* 2680edad6026SXin LI * Set an exit point when SUJ check failed 2681edad6026SXin LI */ 2682edad6026SXin LI retval = setjmp(jmpbuf); 2683edad6026SXin LI if (retval != 0) { 2684edad6026SXin LI pwarn("UNEXPECTED SU+J INCONSISTENCY\n"); 2685edad6026SXin LI TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2686edad6026SXin LI TAILQ_REMOVE(&allsegs, seg, ss_next); 2687edad6026SXin LI free(seg->ss_blk); 2688edad6026SXin LI free(seg); 2689edad6026SXin LI } 2690edad6026SXin LI if (reply("FALLBACK TO FULL FSCK") == 0) { 2691edad6026SXin LI ckfini(0); 2692edad6026SXin LI exit(EEXIT); 2693edad6026SXin LI } else 2694edad6026SXin LI return (-1); 2695edad6026SXin LI } 2696edad6026SXin LI 2697113db2ddSJeff Roberson /* 2698113db2ddSJeff Roberson * Find the journal inode. 2699113db2ddSJeff Roberson */ 2700113db2ddSJeff Roberson ip = ino_read(ROOTINO); 2701113db2ddSJeff Roberson sujino = 0; 2702113db2ddSJeff Roberson ino_visit(ip, ROOTINO, suj_find, 0); 2703edad6026SXin LI if (sujino == 0) { 2704edad6026SXin LI printf("Journal inode removed. Use tunefs to re-create.\n"); 2705edad6026SXin LI sblock.fs_flags &= ~FS_SUJ; 2706edad6026SXin LI sblock.fs_sujfree = 0; 2707edad6026SXin LI return (-1); 2708edad6026SXin LI } 2709113db2ddSJeff Roberson /* 2710113db2ddSJeff Roberson * Fetch the journal inode and verify it. 2711113db2ddSJeff Roberson */ 2712113db2ddSJeff Roberson jip = ino_read(sujino); 2713113db2ddSJeff Roberson printf("** SU+J Recovering %s\n", filesys); 2714113db2ddSJeff Roberson if (suj_verifyino(jip) != 0) 2715113db2ddSJeff Roberson return (-1); 2716113db2ddSJeff Roberson /* 2717113db2ddSJeff Roberson * Build a list of journal blocks in jblocks before parsing the 2718113db2ddSJeff Roberson * available journal blocks in with suj_read(). 2719113db2ddSJeff Roberson */ 2720623d7cb6SMatthew D Fleming printf("** Reading %jd byte journal from inode %ju.\n", 2721623d7cb6SMatthew D Fleming DIP(jip, di_size), (uintmax_t)sujino); 2722113db2ddSJeff Roberson suj_jblocks = jblocks_create(); 2723113db2ddSJeff Roberson blocks = ino_visit(jip, sujino, suj_add_block, 0); 2724edad6026SXin LI if (blocks != numfrags(fs, DIP(jip, di_size))) { 2725623d7cb6SMatthew D Fleming printf("Sparse journal inode %ju.\n", (uintmax_t)sujino); 2726edad6026SXin LI return (-1); 2727edad6026SXin LI } 2728113db2ddSJeff Roberson suj_read(); 2729113db2ddSJeff Roberson jblocks_destroy(suj_jblocks); 2730113db2ddSJeff Roberson suj_jblocks = NULL; 2731113db2ddSJeff Roberson if (preen || reply("RECOVER")) { 2732113db2ddSJeff Roberson printf("** Building recovery table.\n"); 2733113db2ddSJeff Roberson suj_prune(); 2734113db2ddSJeff Roberson suj_build(); 2735113db2ddSJeff Roberson cg_apply(cg_build); 2736113db2ddSJeff Roberson printf("** Resolving unreferenced inode list.\n"); 2737113db2ddSJeff Roberson ino_unlinked(); 2738113db2ddSJeff Roberson printf("** Processing journal entries.\n"); 2739113db2ddSJeff Roberson cg_apply(cg_trunc); 2740113db2ddSJeff Roberson cg_apply(cg_check_blk); 2741364e7245SKonstantin Belousov cg_apply(cg_adj_blk); 2742113db2ddSJeff Roberson cg_apply(cg_check_ino); 2743113db2ddSJeff Roberson } 2744edad6026SXin LI if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0) 2745113db2ddSJeff Roberson return (0); 2746113db2ddSJeff Roberson /* 2747113db2ddSJeff Roberson * To remain idempotent with partial truncations the free bitmaps 2748113db2ddSJeff Roberson * must be written followed by indirect blocks and lastly inode 2749113db2ddSJeff Roberson * blocks. This preserves access to the modified pointers until 2750113db2ddSJeff Roberson * they are freed. 2751113db2ddSJeff Roberson */ 2752113db2ddSJeff Roberson cg_apply(cg_write); 2753113db2ddSJeff Roberson dblk_write(); 2754113db2ddSJeff Roberson cg_apply(cg_write_inos); 2755113db2ddSJeff Roberson /* Write back superblock. */ 2756113db2ddSJeff Roberson closedisk(filesys); 2757edad6026SXin LI if (jrecs > 0 || jbytes > 0) { 2758113db2ddSJeff Roberson printf("** %jd journal records in %jd bytes for %.2f%% utilization\n", 2759113db2ddSJeff Roberson jrecs, jbytes, ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100); 2760113db2ddSJeff Roberson printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd frags.\n", 2761113db2ddSJeff Roberson freeinos, freedir, freeblocks, freefrags); 2762edad6026SXin LI } 2763113db2ddSJeff Roberson 2764113db2ddSJeff Roberson return (0); 2765113db2ddSJeff Roberson } 2766