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