int setup(const char *dev) { #ifndef VERBOSE_BLOCKMAP long bmapsize; #endif struct stat statb; int doskipclean; u_int64_t maxfilesize; int open_flags; struct uvnode *ivp; struct ubuf *bp; int i, isdirty; long sn, curseg; SEGUSE *sup; size_t sumstart; havesb = 0; doskipclean = skipclean; if (stat(dev, &statb) < 0) { pfatal("Can't stat %s: %s\n", dev, strerror(errno)); return (0); } if (!S_ISCHR(statb.st_mode) && skipclean) { pfatal("%s is not a character device", dev); if (reply("CONTINUE") == 0) return (0); } if (nflag) open_flags = O_RDONLY; else open_flags = O_RDWR; if ((fsreadfd = open(dev, open_flags)) < 0) { pfatal("Can't open %s: %s\n", dev, strerror(errno)); return (0); } if (nflag) { if (preen) pfatal("NO WRITE ACCESS"); printf("** %s (NO WRITE)\n", dev); quiet = 0; } else if (!preen && !quiet) printf("** %s\n", dev); fsmodified = 0; lfdir = 0; /* Initialize time in case we have to write */ time(&write_time); bufinit(0); /* XXX we could make a better guess */ fs = lfs_init(fsreadfd, bflag, idaddr, 0, debug); if (fs == NULL) { if (preen) printf("%s: ", cdevname()); errexit("BAD SUPER BLOCK OR IFILE INODE NOT FOUND"); } /* Resize buffer cache now that we have a superblock to guess from. */ bufrehash((lfs_sb_getsegtabsz(fs) + maxino / lfs_sb_getifpb(fs)) << 4); if (lfs_sb_getpflags(fs) & LFS_PF_CLEAN) { if (doskipclean) { if (!quiet) pwarn("%sile system is clean; not checking\n", preen ? "f" : "** F"); return (-1); } if (!preen) pwarn("** File system is already clean\n"); } if (idaddr) { daddr_t tdaddr; SEGSUM *sp; FINFO *fp; int bc; if (debug) pwarn("adjusting offset, serial for -i 0x%jx\n", (uintmax_t)idaddr); tdaddr = lfs_sntod(fs, lfs_dtosn(fs, idaddr)); if (lfs_sntod(fs, lfs_dtosn(fs, tdaddr)) == tdaddr) { if (tdaddr == lfs_sb_gets0addr(fs)) tdaddr += lfs_btofsb(fs, LFS_LABELPAD); for (i = 0; i < LFS_MAXNUMSB; i++) { if (lfs_sb_getsboff(fs, i) == tdaddr) tdaddr += lfs_btofsb(fs, LFS_SBPAD); if (lfs_sb_getsboff(fs, i) > tdaddr) break; } } lfs_sb_setoffset(fs, tdaddr); if (debug) pwarn("begin with offset/serial 0x%jx/%jd\n", (uintmax_t)lfs_sb_getoffset(fs), (intmax_t)lfs_sb_getserial(fs)); while (tdaddr < idaddr) { bread(fs->lfs_devvp, LFS_FSBTODB(fs, tdaddr), lfs_sb_getsumsize(fs), 0, &bp); sp = (SEGSUM *)bp->b_data; sumstart = lfs_ss_getsumstart(fs); if (lfs_ss_getsumsum(fs, sp) != cksum((char *)sp + sumstart, lfs_sb_getsumsize(fs) - sumstart)) { brelse(bp, 0); if (debug) printf("bad cksum at %jx\n", (uintmax_t)tdaddr); break; } fp = SEGSUM_FINFOBASE(fs, sp); bc = howmany(lfs_ss_getninos(fs, sp), LFS_INOPB(fs)) << (lfs_sb_getversion(fs) > 1 ? lfs_sb_getffshift(fs) : lfs_sb_getbshift(fs)); for (i = 0; i < lfs_ss_getnfinfo(fs, sp); i++) { bc += lfs_fi_getlastlength(fs, fp) + ((lfs_fi_getnblocks(fs, fp) - 1) << lfs_sb_getbshift(fs)); fp = NEXT_FINFO(fs, fp); } tdaddr += lfs_btofsb(fs, bc) + 1; lfs_sb_setoffset(fs, tdaddr); lfs_sb_setserial(fs, lfs_ss_getserial(fs, sp) + 1); brelse(bp, 0); } /* * Set curseg, nextseg appropriately -- inlined from * lfs_newseg() */ curseg = lfs_dtosn(fs, lfs_sb_getoffset(fs)); lfs_sb_setcurseg(fs, lfs_sntod(fs, curseg)); for (sn = curseg + lfs_sb_getinterleave(fs);;) { sn = (sn + 1) % lfs_sb_getnseg(fs); if (sn == curseg) errx(1, "init: no clean segments"); LFS_SEGENTRY(sup, fs, sn, bp); isdirty = sup->su_flags & SEGUSE_DIRTY; brelse(bp, 0); if (!isdirty) break; } /* Skip superblock if necessary */ for (i = 0; i < LFS_MAXNUMSB; i++) if (lfs_sb_getoffset(fs) == lfs_sb_getsboff(fs, i)) lfs_sb_addoffset(fs, lfs_btofsb(fs, LFS_SBPAD)); ++fs->lfs_nactive; lfs_sb_setnextseg(fs, lfs_sntod(fs, sn)); if (debug) { pwarn("offset = 0x%" PRIx64 ", serial = %" PRIu64 "\n", lfs_sb_getoffset(fs), lfs_sb_getserial(fs)); pwarn("curseg = %" PRIx64 ", nextseg = %" PRIx64 "\n", lfs_sb_getcurseg(fs), lfs_sb_getnextseg(fs)); } if (!nflag && !skipclean) { lfs_sb_setidaddr(fs, idaddr); fsmodified = 1; sbdirty(); } } if (debug) { pwarn("idaddr = 0x%jx\n", idaddr ? (uintmax_t)idaddr : (uintmax_t)lfs_sb_getidaddr(fs)); pwarn("dev_bsize = %lu\n", dev_bsize); pwarn("lfs_bsize = %lu\n", (unsigned long) lfs_sb_getbsize(fs)); pwarn("lfs_fsize = %lu\n", (unsigned long) lfs_sb_getfsize(fs)); pwarn("lfs_frag = %lu\n", (unsigned long) lfs_sb_getfrag(fs)); pwarn("lfs_inopb = %lu\n", (unsigned long) lfs_sb_getinopb(fs)); } if (lfs_sb_getversion(fs) == 1) maxfsblock = lfs_blkstofrags(fs, lfs_sb_getsize(fs)); else maxfsblock = lfs_sb_getsize(fs); maxfilesize = calcmaxfilesize(lfs_sb_getbshift(fs)); if (/* lfs_sb_getminfree(fs) < 0 || */ lfs_sb_getminfree(fs) > 99) { pfatal("IMPOSSIBLE MINFREE=%u IN SUPERBLOCK", lfs_sb_getminfree(fs)); if (reply("SET TO DEFAULT") == 1) { lfs_sb_setminfree(fs, 10); sbdirty(); } } if (lfs_sb_getbmask(fs) != lfs_sb_getbsize(fs) - 1) { pwarn("INCORRECT BMASK=0x%jx IN SUPERBLOCK (SHOULD BE 0x%x)", (uintmax_t)lfs_sb_getbmask(fs), lfs_sb_getbsize(fs) - 1); lfs_sb_setbmask(fs, lfs_sb_getbsize(fs) - 1); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (lfs_sb_getffmask(fs) != lfs_sb_getfsize(fs) - 1) { pwarn("INCORRECT FFMASK=0x%jx IN SUPERBLOCK (SHOULD BE 0x%x)", (uintmax_t)lfs_sb_getffmask(fs), lfs_sb_getfsize(fs) - 1); lfs_sb_setffmask(fs, lfs_sb_getfsize(fs) - 1); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (lfs_sb_getfbmask(fs) != (1U << lfs_sb_getfbshift(fs)) - 1) { pwarn("INCORRECT FBMASK=0x%jx IN SUPERBLOCK (SHOULD BE 0x%x)", (uintmax_t)lfs_sb_getfbmask(fs), (1U << lfs_sb_getfbshift(fs)) - 1); lfs_sb_setfbmask(fs, (1U << lfs_sb_getfbshift(fs)) - 1); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } if (lfs_sb_getmaxfilesize(fs) != maxfilesize) { pwarn( "INCORRECT MAXFILESIZE=%ju IN SUPERBLOCK (SHOULD BE %ju WITH BSHIFT %u)", (uintmax_t) lfs_sb_getmaxfilesize(fs), (uintmax_t) maxfilesize, lfs_sb_getbshift(fs)); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { lfs_sb_setmaxfilesize(fs, maxfilesize); sbdirty(); } } if (lfs_sb_getmaxsymlinklen(fs) != LFS_MAXSYMLINKLEN(fs)) { pwarn("INCORRECT MAXSYMLINKLEN=%d IN SUPERBLOCK (SHOULD BE %zu)", lfs_sb_getmaxsymlinklen(fs), LFS_MAXSYMLINKLEN(fs)); lfs_sb_setmaxsymlinklen(fs, LFS_MAXSYMLINKLEN(fs)); if (preen) printf(" (FIXED)\n"); if (preen || reply("FIX") == 1) { sbdirty(); } } /* * Read in the Ifile; we'll be using it a lot. * XXX If the Ifile is corrupted we are in bad shape. We need to * XXX run through the segment headers of the entire disk to * XXX reconstruct the inode table, then pretend all segments are * XXX dirty while we do the rest. */ ivp = fs->lfs_ivnode; maxino = ((lfs_dino_getsize(fs, VTOI(ivp)->i_din) - (lfs_sb_getcleansz(fs) + lfs_sb_getsegtabsz(fs)) * lfs_sb_getbsize(fs)) / lfs_sb_getbsize(fs)) * lfs_sb_getifpb(fs); if (debug) pwarn("maxino = %llu\n", (unsigned long long)maxino); for (i = 0; i < lfs_dino_getsize(fs, VTOI(ivp)->i_din); i += lfs_sb_getbsize(fs)) { bread(ivp, i >> lfs_sb_getbshift(fs), lfs_sb_getbsize(fs), 0, &bp); /* XXX check B_ERROR */ brelse(bp, 0); } /* * allocate and initialize the necessary maps */ din_table = ecalloc(maxino, sizeof(*din_table)); seg_table = ecalloc(lfs_sb_getnseg(fs), sizeof(SEGUSE)); /* Get segment flags */ for (i = 0; i < lfs_sb_getnseg(fs); i++) { LFS_SEGENTRY(sup, fs, i, bp); seg_table[i].su_flags = sup->su_flags & ~SEGUSE_ACTIVE; if (preen) seg_table[i].su_nbytes = sup->su_nbytes; brelse(bp, 0); } /* Initialize Ifile entry */ din_table[LFS_IFILE_INUM] = lfs_sb_getidaddr(fs); seg_table[lfs_dtosn(fs, lfs_sb_getidaddr(fs))].su_nbytes += DINOSIZE(fs); #ifndef VERBOSE_BLOCKMAP bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t)); blockmap = ecalloc(bmapsize, sizeof(char)); #else blockmap = ecalloc(maxfsblock, sizeof(ino_t)); #endif statemap = ecalloc(maxino, sizeof(char)); typemap = ecalloc(maxino, sizeof(char)); lncntp = ecalloc(maxino, sizeof(int16_t)); if (preen) { n_files = lfs_sb_getnfiles(fs); n_blks = lfs_sb_getdsize(fs) - lfs_sb_getbfree(fs); numdirs = maxino; inplast = 0; listmax = numdirs + 10; inpsort = ecalloc(listmax, sizeof(struct inoinfo *)); inphead = ecalloc(numdirs, sizeof(struct inoinfo *)); } return (1); ckfini(0); return (0); }
int lfs_truncate(struct vnode *ovp, off_t length, int ioflag, kauth_cred_t cred) { daddr_t lastblock; struct inode *oip = VTOI(ovp); daddr_t bn, lbn, lastiblock[ULFS_NIADDR], indir_lbn[ULFS_NIADDR]; /* XXX ondisk32 */ int32_t newblks[ULFS_NDADDR + ULFS_NIADDR]; struct lfs *fs; struct buf *bp; int offset, size, level; daddr_t count, rcount; daddr_t blocksreleased = 0, real_released = 0; int i, nblocks; int aflags, error, allerror = 0; off_t osize; long lastseg; size_t bc; int obufsize, odb; int usepc; if (ovp->v_type == VCHR || ovp->v_type == VBLK || ovp->v_type == VFIFO || ovp->v_type == VSOCK) { KASSERT(oip->i_size == 0); return 0; } if (length < 0) return (EINVAL); /* * Just return and not update modification times. */ if (oip->i_size == length) { /* still do a uvm_vnp_setsize() as writesize may be larger */ uvm_vnp_setsize(ovp, length); return (0); } fs = oip->i_lfs; if (ovp->v_type == VLNK && (oip->i_size < fs->um_maxsymlinklen || (fs->um_maxsymlinklen == 0 && oip->i_ffs1_blocks == 0))) { #ifdef DIAGNOSTIC if (length != 0) panic("lfs_truncate: partial truncate of symlink"); #endif memset((char *)SHORTLINK(oip), 0, (u_int)oip->i_size); oip->i_size = oip->i_ffs1_size = 0; oip->i_flag |= IN_CHANGE | IN_UPDATE; return (lfs_update(ovp, NULL, NULL, 0)); } if (oip->i_size == length) { oip->i_flag |= IN_CHANGE | IN_UPDATE; return (lfs_update(ovp, NULL, NULL, 0)); } lfs_imtime(fs); osize = oip->i_size; usepc = (ovp->v_type == VREG && ovp != fs->lfs_ivnode); ASSERT_NO_SEGLOCK(fs); /* * Lengthen the size of the file. We must ensure that the * last byte of the file is allocated. Since the smallest * value of osize is 0, length will be at least 1. */ if (osize < length) { if (length > fs->um_maxfilesize) return (EFBIG); aflags = B_CLRBUF; if (ioflag & IO_SYNC) aflags |= B_SYNC; if (usepc) { if (lfs_lblkno(fs, osize) < ULFS_NDADDR && lfs_lblkno(fs, osize) != lfs_lblkno(fs, length) && lfs_blkroundup(fs, osize) != osize) { off_t eob; eob = lfs_blkroundup(fs, osize); uvm_vnp_setwritesize(ovp, eob); error = ulfs_balloc_range(ovp, osize, eob - osize, cred, aflags); if (error) { (void) lfs_truncate(ovp, osize, ioflag & IO_SYNC, cred); return error; } if (ioflag & IO_SYNC) { mutex_enter(ovp->v_interlock); VOP_PUTPAGES(ovp, trunc_page(osize & lfs_sb_getbmask(fs)), round_page(eob), PGO_CLEANIT | PGO_SYNCIO); } } uvm_vnp_setwritesize(ovp, length); error = ulfs_balloc_range(ovp, length - 1, 1, cred, aflags); if (error) { (void) lfs_truncate(ovp, osize, ioflag & IO_SYNC, cred); return error; } uvm_vnp_setsize(ovp, length); oip->i_flag |= IN_CHANGE | IN_UPDATE; KASSERT(ovp->v_size == oip->i_size); oip->i_lfs_hiblk = lfs_lblkno(fs, oip->i_size + lfs_sb_getbsize(fs) - 1) - 1; return (lfs_update(ovp, NULL, NULL, 0)); } else { error = lfs_reserve(fs, ovp, NULL, lfs_btofsb(fs, (ULFS_NIADDR + 2) << lfs_sb_getbshift(fs))); if (error) return (error); error = lfs_balloc(ovp, length - 1, 1, cred, aflags, &bp); lfs_reserve(fs, ovp, NULL, -lfs_btofsb(fs, (ULFS_NIADDR + 2) << lfs_sb_getbshift(fs))); if (error) return (error); oip->i_ffs1_size = oip->i_size = length; uvm_vnp_setsize(ovp, length); (void) VOP_BWRITE(bp->b_vp, bp); oip->i_flag |= IN_CHANGE | IN_UPDATE; oip->i_lfs_hiblk = lfs_lblkno(fs, oip->i_size + lfs_sb_getbsize(fs) - 1) - 1; return (lfs_update(ovp, NULL, NULL, 0)); } } if ((error = lfs_reserve(fs, ovp, NULL, lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs)))) != 0) return (error); /* * Shorten the size of the file. If the file is not being * truncated to a block boundary, the contents of the * partial block following the end of the file must be * zero'ed in case it ever becomes accessible again because * of subsequent file growth. Directories however are not * zero'ed as they should grow back initialized to empty. */ offset = lfs_blkoff(fs, length); lastseg = -1; bc = 0; if (ovp != fs->lfs_ivnode) lfs_seglock(fs, SEGM_PROT); if (offset == 0) { oip->i_size = oip->i_ffs1_size = length; } else if (!usepc) { lbn = lfs_lblkno(fs, length); aflags = B_CLRBUF; if (ioflag & IO_SYNC) aflags |= B_SYNC; error = lfs_balloc(ovp, length - 1, 1, cred, aflags, &bp); if (error) { lfs_reserve(fs, ovp, NULL, -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs))); goto errout; } obufsize = bp->b_bufsize; odb = lfs_btofsb(fs, bp->b_bcount); oip->i_size = oip->i_ffs1_size = length; size = lfs_blksize(fs, oip, lbn); if (ovp->v_type != VDIR) memset((char *)bp->b_data + offset, 0, (u_int)(size - offset)); allocbuf(bp, size, 1); if ((bp->b_flags & B_LOCKED) != 0 && bp->b_iodone == NULL) { mutex_enter(&lfs_lock); locked_queue_bytes -= obufsize - bp->b_bufsize; mutex_exit(&lfs_lock); } if (bp->b_oflags & BO_DELWRI) { lfs_sb_addavail(fs, odb - lfs_btofsb(fs, size)); /* XXX shouldn't this wake up on lfs_availsleep? */ } (void) VOP_BWRITE(bp->b_vp, bp); } else { /* vp->v_type == VREG && length < osize && offset != 0 */ /* * When truncating a regular file down to a non-block-aligned * size, we must zero the part of last block which is past * the new EOF. We must synchronously flush the zeroed pages * to disk since the new pages will be invalidated as soon * as we inform the VM system of the new, smaller size. * We must do this before acquiring the GLOCK, since fetching * the pages will acquire the GLOCK internally. * So there is a window where another thread could see a whole * zeroed page past EOF, but that's life. */ daddr_t xlbn; voff_t eoz; aflags = ioflag & IO_SYNC ? B_SYNC : 0; error = ulfs_balloc_range(ovp, length - 1, 1, cred, aflags); if (error) { lfs_reserve(fs, ovp, NULL, -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs))); goto errout; } xlbn = lfs_lblkno(fs, length); size = lfs_blksize(fs, oip, xlbn); eoz = MIN(lfs_lblktosize(fs, xlbn) + size, osize); ubc_zerorange(&ovp->v_uobj, length, eoz - length, UBC_UNMAP_FLAG(ovp)); if (round_page(eoz) > round_page(length)) { mutex_enter(ovp->v_interlock); error = VOP_PUTPAGES(ovp, round_page(length), round_page(eoz), PGO_CLEANIT | PGO_DEACTIVATE | ((ioflag & IO_SYNC) ? PGO_SYNCIO : 0)); if (error) { lfs_reserve(fs, ovp, NULL, -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs))); goto errout; } } } genfs_node_wrlock(ovp); oip->i_size = oip->i_ffs1_size = length; uvm_vnp_setsize(ovp, length); /* * Calculate index into inode's block list of * last direct and indirect blocks (if any) * which we want to keep. Lastblock is -1 when * the file is truncated to 0. */ /* Avoid sign overflow - XXX assumes that off_t is a quad_t. */ if (length > QUAD_MAX - lfs_sb_getbsize(fs)) lastblock = lfs_lblkno(fs, QUAD_MAX - lfs_sb_getbsize(fs)); else lastblock = lfs_lblkno(fs, length + lfs_sb_getbsize(fs) - 1) - 1; lastiblock[SINGLE] = lastblock - ULFS_NDADDR; lastiblock[DOUBLE] = lastiblock[SINGLE] - LFS_NINDIR(fs); lastiblock[TRIPLE] = lastiblock[DOUBLE] - LFS_NINDIR(fs) * LFS_NINDIR(fs); nblocks = lfs_btofsb(fs, lfs_sb_getbsize(fs)); /* * Record changed file and block pointers before we start * freeing blocks. lastiblock values are also normalized to -1 * for calls to lfs_indirtrunc below. */ memcpy((void *)newblks, (void *)&oip->i_ffs1_db[0], sizeof newblks); for (level = TRIPLE; level >= SINGLE; level--) if (lastiblock[level] < 0) { newblks[ULFS_NDADDR+level] = 0; lastiblock[level] = -1; } for (i = ULFS_NDADDR - 1; i > lastblock; i--) newblks[i] = 0; oip->i_size = oip->i_ffs1_size = osize; error = lfs_vtruncbuf(ovp, lastblock + 1, false, 0); if (error && !allerror) allerror = error; /* * Indirect blocks first. */ indir_lbn[SINGLE] = -ULFS_NDADDR; indir_lbn[DOUBLE] = indir_lbn[SINGLE] - LFS_NINDIR(fs) - 1; indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - LFS_NINDIR(fs) * LFS_NINDIR(fs) - 1; for (level = TRIPLE; level >= SINGLE; level--) { bn = oip->i_ffs1_ib[level]; if (bn != 0) { error = lfs_indirtrunc(oip, indir_lbn[level], bn, lastiblock[level], level, &count, &rcount, &lastseg, &bc); if (error) allerror = error; real_released += rcount; blocksreleased += count; if (lastiblock[level] < 0) { if (oip->i_ffs1_ib[level] > 0) real_released += nblocks; blocksreleased += nblocks; oip->i_ffs1_ib[level] = 0; lfs_blkfree(fs, oip, bn, lfs_sb_getbsize(fs), &lastseg, &bc); lfs_deregister_block(ovp, bn); } } if (lastiblock[level] >= 0) goto done; } /* * All whole direct blocks or frags. */ for (i = ULFS_NDADDR - 1; i > lastblock; i--) { long bsize, obsize; bn = oip->i_ffs1_db[i]; if (bn == 0) continue; bsize = lfs_blksize(fs, oip, i); if (oip->i_ffs1_db[i] > 0) { /* Check for fragment size changes */ obsize = oip->i_lfs_fragsize[i]; real_released += lfs_btofsb(fs, obsize); oip->i_lfs_fragsize[i] = 0; } else obsize = 0; blocksreleased += lfs_btofsb(fs, bsize); oip->i_ffs1_db[i] = 0; lfs_blkfree(fs, oip, bn, obsize, &lastseg, &bc); lfs_deregister_block(ovp, bn); } if (lastblock < 0) goto done; /* * Finally, look for a change in size of the * last direct block; release any frags. */ bn = oip->i_ffs1_db[lastblock]; if (bn != 0) { long oldspace, newspace; #if 0 long olddspace; #endif /* * Calculate amount of space we're giving * back as old block size minus new block size. */ oldspace = lfs_blksize(fs, oip, lastblock); #if 0 olddspace = oip->i_lfs_fragsize[lastblock]; #endif oip->i_size = oip->i_ffs1_size = length; newspace = lfs_blksize(fs, oip, lastblock); if (newspace == 0) panic("itrunc: newspace"); if (oldspace - newspace > 0) { blocksreleased += lfs_btofsb(fs, oldspace - newspace); } #if 0 if (bn > 0 && olddspace - newspace > 0) { /* No segment accounting here, just vnode */ real_released += lfs_btofsb(fs, olddspace - newspace); } #endif } done: /* Finish segment accounting corrections */ lfs_update_seguse(fs, oip, lastseg, bc); #ifdef DIAGNOSTIC for (level = SINGLE; level <= TRIPLE; level++) if ((newblks[ULFS_NDADDR + level] == 0) != ((oip->i_ffs1_ib[level]) == 0)) { panic("lfs itrunc1"); } for (i = 0; i < ULFS_NDADDR; i++) if ((newblks[i] == 0) != (oip->i_ffs1_db[i] == 0)) { panic("lfs itrunc2"); } if (length == 0 && (!LIST_EMPTY(&ovp->v_cleanblkhd) || !LIST_EMPTY(&ovp->v_dirtyblkhd))) panic("lfs itrunc3"); #endif /* DIAGNOSTIC */ /* * Put back the real size. */ oip->i_size = oip->i_ffs1_size = length; oip->i_lfs_effnblks -= blocksreleased; oip->i_ffs1_blocks -= real_released; mutex_enter(&lfs_lock); lfs_sb_addbfree(fs, blocksreleased); mutex_exit(&lfs_lock); #ifdef DIAGNOSTIC if (oip->i_size == 0 && (oip->i_ffs1_blocks != 0 || oip->i_lfs_effnblks != 0)) { printf("lfs_truncate: truncate to 0 but %d blks/%jd effblks\n", oip->i_ffs1_blocks, (intmax_t)oip->i_lfs_effnblks); panic("lfs_truncate: persistent blocks"); } #endif /* * If we truncated to zero, take us off the paging queue. */ mutex_enter(&lfs_lock); if (oip->i_size == 0 && oip->i_flags & IN_PAGING) { oip->i_flags &= ~IN_PAGING; TAILQ_REMOVE(&fs->lfs_pchainhd, oip, i_lfs_pchain); } mutex_exit(&lfs_lock); oip->i_flag |= IN_CHANGE; #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) (void) lfs_chkdq(oip, -blocksreleased, NOCRED, 0); #endif lfs_reserve(fs, ovp, NULL, -lfs_btofsb(fs, (2 * ULFS_NIADDR + 3) << lfs_sb_getbshift(fs))); genfs_node_unlock(ovp); errout: oip->i_lfs_hiblk = lfs_lblkno(fs, oip->i_size + lfs_sb_getbsize(fs) - 1) - 1; if (ovp != fs->lfs_ivnode) lfs_segunlock(fs); return (allerror ? allerror : error); }