1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/param.h> 30*0Sstevel@tonic-gate #include <sys/t_lock.h> 31*0Sstevel@tonic-gate #include <sys/errno.h> 32*0Sstevel@tonic-gate #include <sys/sysmacros.h> 33*0Sstevel@tonic-gate #include <sys/buf.h> 34*0Sstevel@tonic-gate #include <sys/systm.h> 35*0Sstevel@tonic-gate #include <sys/vfs.h> 36*0Sstevel@tonic-gate #include <sys/vnode.h> 37*0Sstevel@tonic-gate #include <sys/kmem.h> 38*0Sstevel@tonic-gate #include <sys/proc.h> 39*0Sstevel@tonic-gate #include <sys/cred.h> 40*0Sstevel@tonic-gate #include <sys/cmn_err.h> 41*0Sstevel@tonic-gate #include <sys/debug.h> 42*0Sstevel@tonic-gate #include <vm/pvn.h> 43*0Sstevel@tonic-gate #include <sys/fs/pc_label.h> 44*0Sstevel@tonic-gate #include <sys/fs/pc_fs.h> 45*0Sstevel@tonic-gate #include <sys/fs/pc_dir.h> 46*0Sstevel@tonic-gate #include <sys/fs/pc_node.h> 47*0Sstevel@tonic-gate #include <sys/dirent.h> 48*0Sstevel@tonic-gate #include <sys/fdio.h> 49*0Sstevel@tonic-gate #include <sys/file.h> 50*0Sstevel@tonic-gate #include <sys/conf.h> 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate struct pchead pcfhead[NPCHASH]; 53*0Sstevel@tonic-gate struct pchead pcdhead[NPCHASH]; 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gate extern krwlock_t pcnodes_lock; 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate static int pc_getentryblock(struct pcnode *, struct buf **); 58*0Sstevel@tonic-gate static int syncpcp(struct pcnode *, int); 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate /* 61*0Sstevel@tonic-gate * fake entry for root directory, since this does not have a parent 62*0Sstevel@tonic-gate * pointing to it. 63*0Sstevel@tonic-gate */ 64*0Sstevel@tonic-gate static struct pcdir rootentry = { 65*0Sstevel@tonic-gate "", 66*0Sstevel@tonic-gate "", 67*0Sstevel@tonic-gate PCA_DIR 68*0Sstevel@tonic-gate }; 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate void 71*0Sstevel@tonic-gate pc_init(void) 72*0Sstevel@tonic-gate { 73*0Sstevel@tonic-gate struct pchead *hdp, *hfp; 74*0Sstevel@tonic-gate int i; 75*0Sstevel@tonic-gate for (i = 0; i < NPCHASH; i++) { 76*0Sstevel@tonic-gate hdp = &pcdhead[i]; 77*0Sstevel@tonic-gate hfp = &pcfhead[i]; 78*0Sstevel@tonic-gate hdp->pch_forw = (struct pcnode *)hdp; 79*0Sstevel@tonic-gate hdp->pch_back = (struct pcnode *)hdp; 80*0Sstevel@tonic-gate hfp->pch_forw = (struct pcnode *)hfp; 81*0Sstevel@tonic-gate hfp->pch_back = (struct pcnode *)hfp; 82*0Sstevel@tonic-gate } 83*0Sstevel@tonic-gate } 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate struct pcnode * 86*0Sstevel@tonic-gate pc_getnode( 87*0Sstevel@tonic-gate struct pcfs *fsp, /* filsystem for node */ 88*0Sstevel@tonic-gate daddr_t blkno, /* phys block no of dir entry */ 89*0Sstevel@tonic-gate int offset, /* offset of dir entry in block */ 90*0Sstevel@tonic-gate struct pcdir *ep) /* node dir entry */ 91*0Sstevel@tonic-gate { 92*0Sstevel@tonic-gate struct pcnode *pcp; 93*0Sstevel@tonic-gate struct pchead *hp; 94*0Sstevel@tonic-gate struct vnode *vp; 95*0Sstevel@tonic-gate pc_cluster32_t scluster; 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 98*0Sstevel@tonic-gate if (ep == (struct pcdir *)0) { 99*0Sstevel@tonic-gate ep = &rootentry; 100*0Sstevel@tonic-gate scluster = 0; 101*0Sstevel@tonic-gate } else { 102*0Sstevel@tonic-gate scluster = pc_getstartcluster(fsp, ep); 103*0Sstevel@tonic-gate } 104*0Sstevel@tonic-gate /* 105*0Sstevel@tonic-gate * First look for active nodes. 106*0Sstevel@tonic-gate * File nodes are identified by the location (blkno, offset) of 107*0Sstevel@tonic-gate * its directory entry. 108*0Sstevel@tonic-gate * Directory nodes are identified by the starting cluster number 109*0Sstevel@tonic-gate * for the entries. 110*0Sstevel@tonic-gate */ 111*0Sstevel@tonic-gate if (ep->pcd_attr & PCA_DIR) { 112*0Sstevel@tonic-gate hp = &pcdhead[PCDHASH(fsp, scluster)]; 113*0Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER); 114*0Sstevel@tonic-gate for (pcp = hp->pch_forw; 115*0Sstevel@tonic-gate pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) { 116*0Sstevel@tonic-gate if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) && 117*0Sstevel@tonic-gate (scluster == pcp->pc_scluster)) { 118*0Sstevel@tonic-gate VN_HOLD(PCTOV(pcp)); 119*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 120*0Sstevel@tonic-gate return (pcp); 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate } 123*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 124*0Sstevel@tonic-gate } else { 125*0Sstevel@tonic-gate hp = &pcfhead[PCFHASH(fsp, blkno, offset)]; 126*0Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_READER); 127*0Sstevel@tonic-gate for (pcp = hp->pch_forw; 128*0Sstevel@tonic-gate pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) { 129*0Sstevel@tonic-gate if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) && 130*0Sstevel@tonic-gate ((pcp->pc_flags & PC_INVAL) == 0) && 131*0Sstevel@tonic-gate (blkno == pcp->pc_eblkno) && 132*0Sstevel@tonic-gate (offset == pcp->pc_eoffset)) { 133*0Sstevel@tonic-gate VN_HOLD(PCTOV(pcp)); 134*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 135*0Sstevel@tonic-gate return (pcp); 136*0Sstevel@tonic-gate } 137*0Sstevel@tonic-gate } 138*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 139*0Sstevel@tonic-gate } 140*0Sstevel@tonic-gate /* 141*0Sstevel@tonic-gate * Cannot find node in active list. Allocate memory for a new node 142*0Sstevel@tonic-gate * initialize it, and put it on the active list. 143*0Sstevel@tonic-gate */ 144*0Sstevel@tonic-gate pcp = kmem_alloc(sizeof (struct pcnode), KM_SLEEP); 145*0Sstevel@tonic-gate bzero(pcp, sizeof (struct pcnode)); 146*0Sstevel@tonic-gate vp = vn_alloc(KM_SLEEP); 147*0Sstevel@tonic-gate pcp->pc_vn = vp; 148*0Sstevel@tonic-gate pcp->pc_entry = *ep; 149*0Sstevel@tonic-gate pcp->pc_eblkno = blkno; 150*0Sstevel@tonic-gate pcp->pc_eoffset = offset; 151*0Sstevel@tonic-gate pcp->pc_scluster = scluster; 152*0Sstevel@tonic-gate pcp->pc_lcluster = scluster; 153*0Sstevel@tonic-gate pcp->pc_lindex = 0; 154*0Sstevel@tonic-gate pcp->pc_flags = 0; 155*0Sstevel@tonic-gate if (ep->pcd_attr & PCA_DIR) { 156*0Sstevel@tonic-gate vn_setops(vp, pcfs_dvnodeops); 157*0Sstevel@tonic-gate vp->v_type = VDIR; 158*0Sstevel@tonic-gate if (scluster == 0) { 159*0Sstevel@tonic-gate vp->v_flag = VROOT; 160*0Sstevel@tonic-gate blkno = offset = 0; 161*0Sstevel@tonic-gate if (IS_FAT32(fsp)) { 162*0Sstevel@tonic-gate pcp->pc_size = pc_fileclsize(fsp, 163*0Sstevel@tonic-gate fsp->pcfs_rdirstart) * fsp->pcfs_clsize; 164*0Sstevel@tonic-gate } else { 165*0Sstevel@tonic-gate pcp->pc_size = 166*0Sstevel@tonic-gate fsp->pcfs_rdirsec * fsp->pcfs_secsize; 167*0Sstevel@tonic-gate } 168*0Sstevel@tonic-gate } else 169*0Sstevel@tonic-gate pcp->pc_size = pc_fileclsize(fsp, scluster) * 170*0Sstevel@tonic-gate fsp->pcfs_clsize; 171*0Sstevel@tonic-gate } else { 172*0Sstevel@tonic-gate vn_setops(vp, pcfs_fvnodeops); 173*0Sstevel@tonic-gate vp->v_type = VREG; 174*0Sstevel@tonic-gate vp->v_flag = VNOSWAP; 175*0Sstevel@tonic-gate fsp->pcfs_frefs++; 176*0Sstevel@tonic-gate pcp->pc_size = ltohi(ep->pcd_size); 177*0Sstevel@tonic-gate } 178*0Sstevel@tonic-gate fsp->pcfs_nrefs++; 179*0Sstevel@tonic-gate vp->v_data = (caddr_t)pcp; 180*0Sstevel@tonic-gate vp->v_vfsp = PCFSTOVFS(fsp); 181*0Sstevel@tonic-gate vn_exists(vp); 182*0Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 183*0Sstevel@tonic-gate insque(pcp, hp); 184*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 185*0Sstevel@tonic-gate return (pcp); 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate int 189*0Sstevel@tonic-gate syncpcp(struct pcnode *pcp, int flags) 190*0Sstevel@tonic-gate { 191*0Sstevel@tonic-gate int err; 192*0Sstevel@tonic-gate if (!vn_has_cached_data(PCTOV(pcp))) 193*0Sstevel@tonic-gate err = 0; 194*0Sstevel@tonic-gate else 195*0Sstevel@tonic-gate err = VOP_PUTPAGE(PCTOV(pcp), (offset_t)0, (uint_t)0, 196*0Sstevel@tonic-gate flags, (struct cred *)0); 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate return (err); 199*0Sstevel@tonic-gate } 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate void 202*0Sstevel@tonic-gate pc_rele(struct pcnode *pcp) 203*0Sstevel@tonic-gate { 204*0Sstevel@tonic-gate struct pcfs *fsp; 205*0Sstevel@tonic-gate struct vnode *vp; 206*0Sstevel@tonic-gate int err; 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate vp = PCTOV(pcp); 209*0Sstevel@tonic-gate PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp); 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate fsp = VFSTOPCFS(vp->v_vfsp); 212*0Sstevel@tonic-gate ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate rw_enter(&pcnodes_lock, RW_WRITER); 215*0Sstevel@tonic-gate pcp->pc_flags |= PC_RELEHOLD; 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate retry: 218*0Sstevel@tonic-gate if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) { 219*0Sstevel@tonic-gate /* 220*0Sstevel@tonic-gate * If the file was removed while active it may be safely 221*0Sstevel@tonic-gate * truncated now. 222*0Sstevel@tonic-gate */ 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) { 225*0Sstevel@tonic-gate (void) pc_truncate(pcp, 0); 226*0Sstevel@tonic-gate } else if (pcp->pc_flags & PC_CHG) { 227*0Sstevel@tonic-gate (void) pc_nodeupdate(pcp); 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate err = syncpcp(pcp, B_INVAL); 230*0Sstevel@tonic-gate if (err) { 231*0Sstevel@tonic-gate (void) syncpcp(pcp, B_INVAL|B_FORCE); 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate } 234*0Sstevel@tonic-gate if (vn_has_cached_data(vp)) { 235*0Sstevel@tonic-gate /* 236*0Sstevel@tonic-gate * pvn_vplist_dirty will abort all old pages 237*0Sstevel@tonic-gate */ 238*0Sstevel@tonic-gate (void) pvn_vplist_dirty(vp, (u_offset_t)0, 239*0Sstevel@tonic-gate pcfs_putapage, B_INVAL, (struct cred *)NULL); 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate (void) pc_syncfat(fsp); 243*0Sstevel@tonic-gate mutex_enter(&vp->v_lock); 244*0Sstevel@tonic-gate if (vn_has_cached_data(vp)) { 245*0Sstevel@tonic-gate mutex_exit(&vp->v_lock); 246*0Sstevel@tonic-gate goto retry; 247*0Sstevel@tonic-gate } 248*0Sstevel@tonic-gate ASSERT(!vn_has_cached_data(vp)); 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate vp->v_count--; /* release our hold from vn_rele */ 251*0Sstevel@tonic-gate if (vp->v_count > 0) { /* Is this check still needed? */ 252*0Sstevel@tonic-gate PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp); 253*0Sstevel@tonic-gate mutex_exit(&vp->v_lock); 254*0Sstevel@tonic-gate pcp->pc_flags &= ~PC_RELEHOLD; 255*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 256*0Sstevel@tonic-gate return; 257*0Sstevel@tonic-gate } 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate remque(pcp); 260*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 261*0Sstevel@tonic-gate if ((vp->v_type == VREG) && !(pcp->pc_flags & PC_INVAL)) { 262*0Sstevel@tonic-gate fsp->pcfs_frefs--; 263*0Sstevel@tonic-gate } 264*0Sstevel@tonic-gate fsp->pcfs_nrefs--; 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate if (fsp->pcfs_nrefs < 0) { 267*0Sstevel@tonic-gate panic("pc_rele: nrefs count"); 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate if (fsp->pcfs_frefs < 0) { 270*0Sstevel@tonic-gate panic("pc_rele: frefs count"); 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate mutex_exit(&vp->v_lock); 274*0Sstevel@tonic-gate vn_invalid(vp); 275*0Sstevel@tonic-gate vn_free(vp); 276*0Sstevel@tonic-gate kmem_free(pcp, sizeof (struct pcnode)); 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate /* 280*0Sstevel@tonic-gate * Mark a pcnode as modified with the current time. 281*0Sstevel@tonic-gate */ 282*0Sstevel@tonic-gate void 283*0Sstevel@tonic-gate pc_mark_mod(struct pcnode *pcp) 284*0Sstevel@tonic-gate { 285*0Sstevel@tonic-gate timestruc_t now; 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate if (PCTOV(pcp)->v_type == VREG) { 288*0Sstevel@tonic-gate gethrestime(&now); 289*0Sstevel@tonic-gate pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime); 290*0Sstevel@tonic-gate pcp->pc_flags |= PC_CHG; 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate } 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gate /* 295*0Sstevel@tonic-gate * Mark a pcnode as accessed with the current time. 296*0Sstevel@tonic-gate */ 297*0Sstevel@tonic-gate void 298*0Sstevel@tonic-gate pc_mark_acc(struct pcnode *pcp) 299*0Sstevel@tonic-gate { 300*0Sstevel@tonic-gate struct pctime pt; 301*0Sstevel@tonic-gate timestruc_t now; 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate if (PCTOV(pcp)->v_type == VREG) { 304*0Sstevel@tonic-gate gethrestime(&now); 305*0Sstevel@tonic-gate pc_tvtopct(&now, &pt); 306*0Sstevel@tonic-gate pcp->pc_entry.pcd_ladate = pt.pct_date; 307*0Sstevel@tonic-gate pcp->pc_flags |= PC_CHG; 308*0Sstevel@tonic-gate } 309*0Sstevel@tonic-gate } 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate /* 312*0Sstevel@tonic-gate * Truncate a file to a length. 313*0Sstevel@tonic-gate * Node must be locked. 314*0Sstevel@tonic-gate */ 315*0Sstevel@tonic-gate int 316*0Sstevel@tonic-gate pc_truncate(struct pcnode *pcp, uint_t length) 317*0Sstevel@tonic-gate { 318*0Sstevel@tonic-gate struct pcfs *fsp; 319*0Sstevel@tonic-gate struct vnode *vp; 320*0Sstevel@tonic-gate int error = 0; 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n", 323*0Sstevel@tonic-gate (void *)pcp, length, pcp->pc_size); 324*0Sstevel@tonic-gate vp = PCTOV(pcp); 325*0Sstevel@tonic-gate if (pcp->pc_flags & PC_INVAL) 326*0Sstevel@tonic-gate return (EIO); 327*0Sstevel@tonic-gate fsp = VFSTOPCFS(vp->v_vfsp); 328*0Sstevel@tonic-gate /* 329*0Sstevel@tonic-gate * directories are always truncated to zero and are not marked 330*0Sstevel@tonic-gate */ 331*0Sstevel@tonic-gate if (vp->v_type == VDIR) { 332*0Sstevel@tonic-gate error = pc_bfree(pcp, 0); 333*0Sstevel@tonic-gate return (error); 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate /* 336*0Sstevel@tonic-gate * If length is the same as the current size 337*0Sstevel@tonic-gate * just mark the pcnode and return. 338*0Sstevel@tonic-gate */ 339*0Sstevel@tonic-gate if (length > pcp->pc_size) { 340*0Sstevel@tonic-gate daddr_t bno; 341*0Sstevel@tonic-gate uint_t llcn; 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate /* 344*0Sstevel@tonic-gate * We are extending a file. 345*0Sstevel@tonic-gate * Extend it with _one_ call to pc_balloc (no holes) 346*0Sstevel@tonic-gate * since we don't need to use the block number(s). 347*0Sstevel@tonic-gate */ 348*0Sstevel@tonic-gate if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) < 349*0Sstevel@tonic-gate (llcn = (daddr_t)howmany((offset_t)length, 350*0Sstevel@tonic-gate fsp->pcfs_clsize))) { 351*0Sstevel@tonic-gate error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno); 352*0Sstevel@tonic-gate } 353*0Sstevel@tonic-gate if (error) { 354*0Sstevel@tonic-gate PC_DPRINTF1(2, "pc_truncate: error=%d\n", error); 355*0Sstevel@tonic-gate /* 356*0Sstevel@tonic-gate * probably ran out disk space; 357*0Sstevel@tonic-gate * determine current file size 358*0Sstevel@tonic-gate */ 359*0Sstevel@tonic-gate pcp->pc_size = fsp->pcfs_clsize * 360*0Sstevel@tonic-gate pc_fileclsize(fsp, pcp->pc_scluster); 361*0Sstevel@tonic-gate } else 362*0Sstevel@tonic-gate pcp->pc_size = length; 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate } else if (length < pcp->pc_size) { 365*0Sstevel@tonic-gate /* 366*0Sstevel@tonic-gate * We are shrinking a file. 367*0Sstevel@tonic-gate * Free blocks after the block that length points to. 368*0Sstevel@tonic-gate */ 369*0Sstevel@tonic-gate if (pc_blkoff(fsp, length) == 0) { 370*0Sstevel@tonic-gate /* 371*0Sstevel@tonic-gate * Truncation to a block (cluster size) boundary only 372*0Sstevel@tonic-gate * requires us to invalidate everything after the new 373*0Sstevel@tonic-gate * end of the file. 374*0Sstevel@tonic-gate */ 375*0Sstevel@tonic-gate (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length, 376*0Sstevel@tonic-gate pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 377*0Sstevel@tonic-gate } else { 378*0Sstevel@tonic-gate /* 379*0Sstevel@tonic-gate * pvn_vpzero() cannot deal with more than MAXBSIZE 380*0Sstevel@tonic-gate * chunks. Since the FAT clustersize can get larger 381*0Sstevel@tonic-gate * than that, we'll zero from the new length to the 382*0Sstevel@tonic-gate * end of the cluster for clustersizes smaller than 383*0Sstevel@tonic-gate * MAXBSIZE - or the end of the MAXBSIZE block in 384*0Sstevel@tonic-gate * case we've got a large clustersize. 385*0Sstevel@tonic-gate */ 386*0Sstevel@tonic-gate size_t nbytes = 387*0Sstevel@tonic-gate roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) - 388*0Sstevel@tonic-gate length; 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes); 391*0Sstevel@tonic-gate (void) pvn_vplist_dirty(PCTOV(pcp), 392*0Sstevel@tonic-gate (u_offset_t)length + nbytes, 393*0Sstevel@tonic-gate pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate error = pc_bfree(pcp, 396*0Sstevel@tonic-gate (pc_cluster32_t)howmany((offset_t)length, 397*0Sstevel@tonic-gate fsp->pcfs_clsize)); 398*0Sstevel@tonic-gate pcp->pc_size = length; 399*0Sstevel@tonic-gate } 400*0Sstevel@tonic-gate pc_mark_mod(pcp); 401*0Sstevel@tonic-gate return (error); 402*0Sstevel@tonic-gate } 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate /* 405*0Sstevel@tonic-gate * Get block for entry. 406*0Sstevel@tonic-gate */ 407*0Sstevel@tonic-gate static int 408*0Sstevel@tonic-gate pc_getentryblock(struct pcnode *pcp, struct buf **bpp) 409*0Sstevel@tonic-gate { 410*0Sstevel@tonic-gate struct pcfs *fsp; 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate PC_DPRINTF0(7, "pc_getentryblock "); 413*0Sstevel@tonic-gate fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp); 414*0Sstevel@tonic-gate if (pcp->pc_eblkno >= fsp->pcfs_datastart || 415*0Sstevel@tonic-gate (pcp->pc_eblkno - fsp->pcfs_rdirstart) < 416*0Sstevel@tonic-gate (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 417*0Sstevel@tonic-gate *bpp = bread(fsp->pcfs_xdev, 418*0Sstevel@tonic-gate pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize); 419*0Sstevel@tonic-gate } else { 420*0Sstevel@tonic-gate *bpp = bread(fsp->pcfs_xdev, 421*0Sstevel@tonic-gate pc_dbdaddr(fsp, pcp->pc_eblkno), 422*0Sstevel@tonic-gate (int)(fsp->pcfs_datastart-pcp->pc_eblkno) * 423*0Sstevel@tonic-gate fsp->pcfs_secsize); 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate if ((*bpp)->b_flags & B_ERROR) { 426*0Sstevel@tonic-gate PC_DPRINTF0(1, "pc_getentryblock: error "); 427*0Sstevel@tonic-gate brelse(*bpp); 428*0Sstevel@tonic-gate pc_mark_irrecov(fsp); 429*0Sstevel@tonic-gate return (EIO); 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate return (0); 432*0Sstevel@tonic-gate } 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate /* 435*0Sstevel@tonic-gate * Sync all data associated with a file. 436*0Sstevel@tonic-gate * Flush all the blocks in the buffer cache out to disk, sync the FAT and 437*0Sstevel@tonic-gate * update the directory entry. 438*0Sstevel@tonic-gate */ 439*0Sstevel@tonic-gate int 440*0Sstevel@tonic-gate pc_nodesync(struct pcnode *pcp) 441*0Sstevel@tonic-gate { 442*0Sstevel@tonic-gate struct pcfs *fsp; 443*0Sstevel@tonic-gate int err; 444*0Sstevel@tonic-gate struct vnode *vp; 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate PC_DPRINTF1(7, "pc_nodesync pcp=0x%p\n", (void *)pcp); 447*0Sstevel@tonic-gate vp = PCTOV(pcp); 448*0Sstevel@tonic-gate fsp = VFSTOPCFS(vp->v_vfsp); 449*0Sstevel@tonic-gate err = 0; 450*0Sstevel@tonic-gate if (pcp->pc_flags & PC_MOD) { 451*0Sstevel@tonic-gate /* 452*0Sstevel@tonic-gate * Flush all data blocks from buffer cache and 453*0Sstevel@tonic-gate * update the FAT which points to the data. 454*0Sstevel@tonic-gate */ 455*0Sstevel@tonic-gate if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */ 456*0Sstevel@tonic-gate if (err == ENOMEM) 457*0Sstevel@tonic-gate return (err); 458*0Sstevel@tonic-gate else { 459*0Sstevel@tonic-gate pc_mark_irrecov(fsp); 460*0Sstevel@tonic-gate return (EIO); 461*0Sstevel@tonic-gate } 462*0Sstevel@tonic-gate } 463*0Sstevel@tonic-gate pcp->pc_flags &= ~PC_MOD; 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate /* 466*0Sstevel@tonic-gate * update the directory entry 467*0Sstevel@tonic-gate */ 468*0Sstevel@tonic-gate if (pcp->pc_flags & PC_CHG) 469*0Sstevel@tonic-gate (void) pc_nodeupdate(pcp); 470*0Sstevel@tonic-gate return (err); 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate /* 474*0Sstevel@tonic-gate * Update the node's directory entry. 475*0Sstevel@tonic-gate */ 476*0Sstevel@tonic-gate int 477*0Sstevel@tonic-gate pc_nodeupdate(struct pcnode *pcp) 478*0Sstevel@tonic-gate { 479*0Sstevel@tonic-gate struct buf *bp; 480*0Sstevel@tonic-gate int error; 481*0Sstevel@tonic-gate struct vnode *vp; 482*0Sstevel@tonic-gate struct pcfs *fsp; 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate vp = PCTOV(pcp); 485*0Sstevel@tonic-gate fsp = VFSTOPCFS(vp->v_vfsp); 486*0Sstevel@tonic-gate if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) { 487*0Sstevel@tonic-gate /* no node to update */ 488*0Sstevel@tonic-gate pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 489*0Sstevel@tonic-gate return (0); 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate if (vp->v_flag & VROOT) { 492*0Sstevel@tonic-gate panic("pc_nodeupdate"); 493*0Sstevel@tonic-gate } 494*0Sstevel@tonic-gate if (pcp->pc_flags & PC_INVAL) 495*0Sstevel@tonic-gate return (0); 496*0Sstevel@tonic-gate PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp, 497*0Sstevel@tonic-gate pcp->pc_eblkno, pcp->pc_eoffset); 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate if (error = pc_getentryblock(pcp, &bp)) { 500*0Sstevel@tonic-gate return (error); 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate if (vp->v_type == VREG) { 503*0Sstevel@tonic-gate if (pcp->pc_flags & PC_CHG) 504*0Sstevel@tonic-gate pcp->pc_entry.pcd_attr |= PCA_ARCH; 505*0Sstevel@tonic-gate pcp->pc_entry.pcd_size = htoli(pcp->pc_size); 506*0Sstevel@tonic-gate } 507*0Sstevel@tonic-gate pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster); 508*0Sstevel@tonic-gate *((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry; 509*0Sstevel@tonic-gate bwrite2(bp); 510*0Sstevel@tonic-gate error = geterror(bp); 511*0Sstevel@tonic-gate if (error) 512*0Sstevel@tonic-gate error = EIO; 513*0Sstevel@tonic-gate brelse(bp); 514*0Sstevel@tonic-gate if (error) { 515*0Sstevel@tonic-gate PC_DPRINTF0(1, "pc_nodeupdate ERROR\n"); 516*0Sstevel@tonic-gate pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp)); 517*0Sstevel@tonic-gate } 518*0Sstevel@tonic-gate pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 519*0Sstevel@tonic-gate return (error); 520*0Sstevel@tonic-gate } 521*0Sstevel@tonic-gate 522*0Sstevel@tonic-gate /* 523*0Sstevel@tonic-gate * Verify that the disk in the drive is the same one that we 524*0Sstevel@tonic-gate * got the pcnode from. 525*0Sstevel@tonic-gate * MUST be called with node unlocked. 526*0Sstevel@tonic-gate */ 527*0Sstevel@tonic-gate /* ARGSUSED */ 528*0Sstevel@tonic-gate int 529*0Sstevel@tonic-gate pc_verify(struct pcfs *fsp) 530*0Sstevel@tonic-gate { 531*0Sstevel@tonic-gate int fdstatus = 0; 532*0Sstevel@tonic-gate int error = 0; 533*0Sstevel@tonic-gate 534*0Sstevel@tonic-gate if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV) 535*0Sstevel@tonic-gate return (EIO); 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) { 538*0Sstevel@tonic-gate PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp); 539*0Sstevel@tonic-gate error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev, 540*0Sstevel@tonic-gate FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE|FKIOCTL, 541*0Sstevel@tonic-gate NULL, NULL); 542*0Sstevel@tonic-gate 543*0Sstevel@tonic-gate if (error) { 544*0Sstevel@tonic-gate if (error == ENOTTY || error == ENXIO) { 545*0Sstevel@tonic-gate error = 0; 546*0Sstevel@tonic-gate } else { 547*0Sstevel@tonic-gate PC_DPRINTF1(1, 548*0Sstevel@tonic-gate "pc_verify: FDGETCHANGE ioctl failed: %d\n", 549*0Sstevel@tonic-gate error); 550*0Sstevel@tonic-gate pc_mark_irrecov(fsp); 551*0Sstevel@tonic-gate } 552*0Sstevel@tonic-gate } else if (fsp->pcfs_fatjustread) { 553*0Sstevel@tonic-gate /* 554*0Sstevel@tonic-gate * Ignore the results of the ioctl if we just 555*0Sstevel@tonic-gate * read the FAT. There is a good chance that 556*0Sstevel@tonic-gate * the disk changed bit will be on, because 557*0Sstevel@tonic-gate * we've just mounted and we don't want to 558*0Sstevel@tonic-gate * give a false positive that the sky is falling. 559*0Sstevel@tonic-gate */ 560*0Sstevel@tonic-gate fsp->pcfs_fatjustread = 0; 561*0Sstevel@tonic-gate } else { 562*0Sstevel@tonic-gate /* 563*0Sstevel@tonic-gate * Oddly enough we can't check just one flag here. The 564*0Sstevel@tonic-gate * x86 floppy driver sets a different flag 565*0Sstevel@tonic-gate * (FDGC_DETECTED) than the sparc driver does. 566*0Sstevel@tonic-gate * I think this MAY be a bug, and I filed 4165938 567*0Sstevel@tonic-gate * to get someone to look at the behavior 568*0Sstevel@tonic-gate * a bit more closely. In the meantime, my testing and 569*0Sstevel@tonic-gate * code examination seem to indicate it is safe to 570*0Sstevel@tonic-gate * check for either bit being set. 571*0Sstevel@tonic-gate */ 572*0Sstevel@tonic-gate if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) { 573*0Sstevel@tonic-gate PC_DPRINTF0(1, "pc_verify: change detected\n"); 574*0Sstevel@tonic-gate pc_mark_irrecov(fsp); 575*0Sstevel@tonic-gate } 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate } 578*0Sstevel@tonic-gate if (!(error || fsp->pcfs_fatp)) { 579*0Sstevel@tonic-gate error = pc_getfat(fsp); 580*0Sstevel@tonic-gate } 581*0Sstevel@tonic-gate 582*0Sstevel@tonic-gate return (error); 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate /* 586*0Sstevel@tonic-gate * The disk has changed, pulling the rug out from beneath us. 587*0Sstevel@tonic-gate * Mark the FS as being in an irrecoverable state. 588*0Sstevel@tonic-gate * In a short while we'll clean up. 589*0Sstevel@tonic-gate */ 590*0Sstevel@tonic-gate void 591*0Sstevel@tonic-gate pc_mark_irrecov(struct pcfs *fsp) 592*0Sstevel@tonic-gate { 593*0Sstevel@tonic-gate if (!(fsp->pcfs_flags & PCFS_NOCHK)) { 594*0Sstevel@tonic-gate if (pc_lockfs(fsp, 1, 0)) { 595*0Sstevel@tonic-gate /* 596*0Sstevel@tonic-gate * Locking failed, which currently would 597*0Sstevel@tonic-gate * only happen if the FS were already 598*0Sstevel@tonic-gate * marked as hosed. If another reason for 599*0Sstevel@tonic-gate * failure were to arise in the future, this 600*0Sstevel@tonic-gate * routine would have to change. 601*0Sstevel@tonic-gate */ 602*0Sstevel@tonic-gate return; 603*0Sstevel@tonic-gate } 604*0Sstevel@tonic-gate 605*0Sstevel@tonic-gate fsp->pcfs_flags |= PCFS_IRRECOV; 606*0Sstevel@tonic-gate cmn_err(CE_WARN, 607*0Sstevel@tonic-gate "Disk was changed during an update or\n" 608*0Sstevel@tonic-gate "an irrecoverable error was encountered.\n" 609*0Sstevel@tonic-gate "File damage is possible. To prevent further\n" 610*0Sstevel@tonic-gate "damage, this pcfs instance will now be frozen.\n" 611*0Sstevel@tonic-gate "Use umount(1M) to release the instance.\n"); 612*0Sstevel@tonic-gate (void) pc_unlockfs(fsp); 613*0Sstevel@tonic-gate } 614*0Sstevel@tonic-gate } 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate /* 617*0Sstevel@tonic-gate * The disk has been changed! 618*0Sstevel@tonic-gate */ 619*0Sstevel@tonic-gate void 620*0Sstevel@tonic-gate pc_diskchanged(struct pcfs *fsp) 621*0Sstevel@tonic-gate { 622*0Sstevel@tonic-gate struct pcnode *pcp, *npcp = NULL; 623*0Sstevel@tonic-gate struct pchead *hp; 624*0Sstevel@tonic-gate struct vnode *vp; 625*0Sstevel@tonic-gate extern vfs_t EIO_vfs; 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate /* 628*0Sstevel@tonic-gate * Eliminate all pcnodes (dir & file) associated with this fs. 629*0Sstevel@tonic-gate * If the node is internal, ie, no references outside of 630*0Sstevel@tonic-gate * pcfs itself, then release the associated vnode structure. 631*0Sstevel@tonic-gate * Invalidate the in core FAT. 632*0Sstevel@tonic-gate * Invalidate cached data blocks and blocks waiting for I/O. 633*0Sstevel@tonic-gate */ 634*0Sstevel@tonic-gate PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp); 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) { 637*0Sstevel@tonic-gate for (pcp = hp->pch_forw; 638*0Sstevel@tonic-gate pcp != (struct pcnode *)hp; pcp = npcp) { 639*0Sstevel@tonic-gate npcp = pcp -> pc_forw; 640*0Sstevel@tonic-gate vp = PCTOV(pcp); 641*0Sstevel@tonic-gate if (VFSTOPCFS(vp->v_vfsp) == fsp && 642*0Sstevel@tonic-gate !(pcp->pc_flags & PC_RELEHOLD)) { 643*0Sstevel@tonic-gate mutex_enter(&(vp)->v_lock); 644*0Sstevel@tonic-gate if (vp->v_count > 0) { 645*0Sstevel@tonic-gate mutex_exit(&(vp)->v_lock); 646*0Sstevel@tonic-gate continue; 647*0Sstevel@tonic-gate } 648*0Sstevel@tonic-gate mutex_exit(&(vp)->v_lock); 649*0Sstevel@tonic-gate VN_HOLD(vp); 650*0Sstevel@tonic-gate remque(pcp); 651*0Sstevel@tonic-gate vp->v_data = NULL; 652*0Sstevel@tonic-gate vp->v_vfsp = &EIO_vfs; 653*0Sstevel@tonic-gate vp->v_type = VBAD; 654*0Sstevel@tonic-gate VN_RELE(vp); 655*0Sstevel@tonic-gate if (!(pcp->pc_flags & PC_EXTERNAL)) 656*0Sstevel@tonic-gate vn_free(vp); 657*0Sstevel@tonic-gate kmem_free(pcp, sizeof (struct pcnode)); 658*0Sstevel@tonic-gate fsp->pcfs_nrefs --; 659*0Sstevel@tonic-gate } 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate } 662*0Sstevel@tonic-gate for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) { 663*0Sstevel@tonic-gate for (pcp = hp->pch_forw; fsp->pcfs_frefs && 664*0Sstevel@tonic-gate pcp != (struct pcnode *)hp; pcp = npcp) { 665*0Sstevel@tonic-gate npcp = pcp -> pc_forw; 666*0Sstevel@tonic-gate vp = PCTOV(pcp); 667*0Sstevel@tonic-gate if (VFSTOPCFS(vp->v_vfsp) == fsp && 668*0Sstevel@tonic-gate !(pcp->pc_flags & PC_RELEHOLD)) { 669*0Sstevel@tonic-gate mutex_enter(&(vp)->v_lock); 670*0Sstevel@tonic-gate if (vp->v_count > 0) { 671*0Sstevel@tonic-gate mutex_exit(&(vp)->v_lock); 672*0Sstevel@tonic-gate continue; 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate mutex_exit(&(vp)->v_lock); 675*0Sstevel@tonic-gate VN_HOLD(vp); 676*0Sstevel@tonic-gate remque(pcp); 677*0Sstevel@tonic-gate vp->v_data = NULL; 678*0Sstevel@tonic-gate vp->v_vfsp = &EIO_vfs; 679*0Sstevel@tonic-gate vp->v_type = VBAD; 680*0Sstevel@tonic-gate VN_RELE(vp); 681*0Sstevel@tonic-gate if (!(pcp->pc_flags & PC_EXTERNAL)) 682*0Sstevel@tonic-gate vn_free(vp); 683*0Sstevel@tonic-gate kmem_free(pcp, sizeof (struct pcnode)); 684*0Sstevel@tonic-gate fsp->pcfs_frefs --; 685*0Sstevel@tonic-gate fsp->pcfs_nrefs --; 686*0Sstevel@tonic-gate } 687*0Sstevel@tonic-gate } 688*0Sstevel@tonic-gate } 689*0Sstevel@tonic-gate #ifdef undef 690*0Sstevel@tonic-gate if (fsp->pcfs_frefs) { 691*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 692*0Sstevel@tonic-gate panic("pc_diskchanged: frefs"); 693*0Sstevel@tonic-gate } 694*0Sstevel@tonic-gate if (fsp->pcfs_nrefs) { 695*0Sstevel@tonic-gate rw_exit(&pcnodes_lock); 696*0Sstevel@tonic-gate panic("pc_diskchanged: nrefs"); 697*0Sstevel@tonic-gate } 698*0Sstevel@tonic-gate #endif 699*0Sstevel@tonic-gate if (fsp->pcfs_fatp != (uchar_t *)0) { 700*0Sstevel@tonic-gate pc_invalfat(fsp); 701*0Sstevel@tonic-gate } else { 702*0Sstevel@tonic-gate binval(fsp->pcfs_xdev); 703*0Sstevel@tonic-gate } 704*0Sstevel@tonic-gate } 705