xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_node.c (revision dd4eeefd)
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 2007 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), 0, 0, flags, kcred);
211 
212 	return (err);
213 }
214 
215 void
216 pc_rele(struct pcnode *pcp)
217 {
218 	struct pcfs *fsp;
219 	struct vnode *vp;
220 	int err;
221 
222 	vp = PCTOV(pcp);
223 	PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp);
224 
225 	fsp = VFSTOPCFS(vp->v_vfsp);
226 	ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
227 
228 	rw_enter(&pcnodes_lock, RW_WRITER);
229 	pcp->pc_flags |= PC_RELEHOLD;
230 
231 retry:
232 	if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) {
233 		/*
234 		 * If the file was removed while active it may be safely
235 		 * truncated now.
236 		 */
237 
238 		if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) {
239 			(void) pc_truncate(pcp, 0);
240 		} else if (pcp->pc_flags & PC_CHG) {
241 			(void) pc_nodeupdate(pcp);
242 		}
243 		err = syncpcp(pcp, B_INVAL);
244 		if (err) {
245 			(void) syncpcp(pcp, B_INVAL|B_FORCE);
246 		}
247 	}
248 	if (vn_has_cached_data(vp)) {
249 		/*
250 		 * pvn_vplist_dirty will abort all old pages
251 		 */
252 		(void) pvn_vplist_dirty(vp, (u_offset_t)0,
253 		    pcfs_putapage, B_INVAL, (struct cred *)NULL);
254 	}
255 
256 	(void) pc_syncfat(fsp);
257 	mutex_enter(&vp->v_lock);
258 	if (vn_has_cached_data(vp)) {
259 		mutex_exit(&vp->v_lock);
260 		goto retry;
261 	}
262 	ASSERT(!vn_has_cached_data(vp));
263 
264 	vp->v_count--;  /* release our hold from vn_rele */
265 	if (vp->v_count > 0) { /* Is this check still needed? */
266 		PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp);
267 		mutex_exit(&vp->v_lock);
268 		pcp->pc_flags &= ~PC_RELEHOLD;
269 		rw_exit(&pcnodes_lock);
270 		return;
271 	}
272 
273 	remque(pcp);
274 	rw_exit(&pcnodes_lock);
275 	/*
276 	 * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL)
277 	 * here. Seems superfluous/incorrect, but then earlier on PC_INVAL
278 	 * was never set anywhere in PCFS. Now it is, and we _have_ to drop
279 	 * the file reference here. Else, we'd screw up umount/modunload.
280 	 */
281 	if ((vp->v_type == VREG)) {
282 		fsp->pcfs_frefs--;
283 	}
284 	fsp->pcfs_nrefs--;
285 	VFS_RELE(vp->v_vfsp);
286 
287 	if (fsp->pcfs_nrefs < 0) {
288 		panic("pc_rele: nrefs count");
289 	}
290 	if (fsp->pcfs_frefs < 0) {
291 		panic("pc_rele: frefs count");
292 	}
293 
294 	mutex_exit(&vp->v_lock);
295 	vn_invalid(vp);
296 	vn_free(vp);
297 	kmem_free(pcp, sizeof (struct pcnode));
298 }
299 
300 /*
301  * Mark a pcnode as modified with the current time.
302  */
303 void
304 pc_mark_mod(struct pcnode *pcp)
305 {
306 	timestruc_t now;
307 
308 	if (PCTOV(pcp)->v_type == VREG) {
309 		gethrestime(&now);
310 		if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
311 			PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
312 			    "conversion, curtime = %lld\n",
313 			    (long long)now.tv_sec);
314 		pcp->pc_flags |= PC_CHG;
315 	}
316 }
317 
318 /*
319  * Mark a pcnode as accessed with the current time.
320  */
321 void
322 pc_mark_acc(struct pcnode *pcp)
323 {
324 	struct pctime pt = { 0, 0 };
325 	timestruc_t now;
326 
327 	if (PCTOV(pcp)->v_type == VREG) {
328 		gethrestime(&now);
329 		if (pc_tvtopct(&now, &pt))
330 			PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
331 			    "conversion, curtime = %lld\n",
332 			    (long long)now.tv_sec);
333 		pcp->pc_entry.pcd_ladate = pt.pct_date;
334 		pcp->pc_flags |= PC_CHG;
335 	}
336 }
337 
338 /*
339  * Truncate a file to a length.
340  * Node must be locked.
341  */
342 int
343 pc_truncate(struct pcnode *pcp, uint_t length)
344 {
345 	struct pcfs *fsp;
346 	struct vnode *vp;
347 	int error = 0;
348 
349 	PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n",
350 	    (void *)pcp, length, pcp->pc_size);
351 	vp = PCTOV(pcp);
352 	if (pcp->pc_flags & PC_INVAL)
353 		return (EIO);
354 	fsp = VFSTOPCFS(vp->v_vfsp);
355 	/*
356 	 * directories are always truncated to zero and are not marked
357 	 */
358 	if (vp->v_type == VDIR) {
359 		error = pc_bfree(pcp, 0);
360 		return (error);
361 	}
362 	/*
363 	 * If length is the same as the current size
364 	 * just mark the pcnode and return.
365 	 */
366 	if (length > pcp->pc_size) {
367 		daddr_t bno;
368 		uint_t llcn;
369 
370 		/*
371 		 * We are extending a file.
372 		 * Extend it with _one_ call to pc_balloc (no holes)
373 		 * since we don't need to use the block number(s).
374 		 */
375 		if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) <
376 		    (llcn = (daddr_t)howmany((offset_t)length,
377 				fsp->pcfs_clsize))) {
378 			error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno);
379 		}
380 		if (error) {
381 			pc_cluster32_t ncl = 0;
382 			PC_DPRINTF1(2, "pc_truncate: error=%d\n", error);
383 			/*
384 			 * probably ran out disk space;
385 			 * determine current file size
386 			 */
387 			if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) {
388 				PC_DPRINTF1(2, "cluster chain corruption, "
389 				    "scluster=%d\n", pcp->pc_scluster);
390 				pcp->pc_flags |= PC_INVAL;
391 			}
392 			pcp->pc_size = fsp->pcfs_clsize * ncl;
393 		} else
394 			pcp->pc_size = length;
395 
396 	} else if (length < pcp->pc_size) {
397 		/*
398 		 * We are shrinking a file.
399 		 * Free blocks after the block that length points to.
400 		 */
401 		if (pc_blkoff(fsp, length) == 0) {
402 			/*
403 			 * Truncation to a block (cluster size) boundary only
404 			 * requires us to invalidate everything after the new
405 			 * end of the file.
406 			 */
407 			(void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length,
408 				pcfs_putapage, B_INVAL | B_TRUNC, CRED());
409 		} else {
410 			/*
411 			 * pvn_vpzero() cannot deal with more than MAXBSIZE
412 			 * chunks. Since the FAT clustersize can get larger
413 			 * than that, we'll zero from the new length to the
414 			 * end of the cluster for clustersizes smaller than
415 			 * MAXBSIZE - or the end of the MAXBSIZE block in
416 			 * case we've got a large clustersize.
417 			 */
418 			size_t nbytes =
419 			    roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) -
420 			    length;
421 
422 			pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes);
423 			(void) pvn_vplist_dirty(PCTOV(pcp),
424 			    (u_offset_t)length + nbytes,
425 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
426 		}
427 		error = pc_bfree(pcp,
428 		    (pc_cluster32_t)howmany((offset_t)length,
429 			    fsp->pcfs_clsize));
430 		pcp->pc_size = length;
431 	}
432 	pc_mark_mod(pcp);
433 	return (error);
434 }
435 
436 /*
437  * Get block for entry.
438  */
439 static int
440 pc_getentryblock(struct pcnode *pcp, struct buf **bpp)
441 {
442 	struct pcfs *fsp;
443 
444 	PC_DPRINTF0(7, "pc_getentryblock ");
445 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
446 	if (pcp->pc_eblkno >= fsp->pcfs_datastart ||
447 	    (pcp->pc_eblkno - fsp->pcfs_rdirstart) <
448 	    (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
449 		*bpp = bread(fsp->pcfs_xdev,
450 		    pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize);
451 	} else {
452 		*bpp = bread(fsp->pcfs_xdev,
453 		    pc_dbdaddr(fsp, pcp->pc_eblkno),
454 		    (int)(fsp->pcfs_datastart-pcp->pc_eblkno) *
455 		    fsp->pcfs_secsize);
456 	}
457 	if ((*bpp)->b_flags & B_ERROR) {
458 		PC_DPRINTF0(1, "pc_getentryblock: error ");
459 		brelse(*bpp);
460 		pc_mark_irrecov(fsp);
461 		return (EIO);
462 	}
463 	return (0);
464 }
465 
466 /*
467  * Sync all data associated with a file.
468  * Flush all the blocks in the buffer cache out to disk, sync the FAT and
469  * update the directory entry.
470  */
471 int
472 pc_nodesync(struct pcnode *pcp)
473 {
474 	struct pcfs *fsp;
475 	int err;
476 	struct vnode *vp;
477 
478 	PC_DPRINTF1(7, "pc_nodesync pcp=0x%p\n", (void *)pcp);
479 	vp = PCTOV(pcp);
480 	fsp = VFSTOPCFS(vp->v_vfsp);
481 	err = 0;
482 	if (pcp->pc_flags & PC_MOD) {
483 		/*
484 		 * Flush all data blocks from buffer cache and
485 		 * update the FAT which points to the data.
486 		 */
487 		if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */
488 			if (err == ENOMEM)
489 				return (err);
490 			else {
491 				pc_mark_irrecov(fsp);
492 				return (EIO);
493 			}
494 		}
495 		pcp->pc_flags &= ~PC_MOD;
496 	}
497 	/*
498 	 * update the directory entry
499 	 */
500 	if (pcp->pc_flags & PC_CHG)
501 		(void) pc_nodeupdate(pcp);
502 	return (err);
503 }
504 
505 /*
506  * Update the node's directory entry.
507  */
508 int
509 pc_nodeupdate(struct pcnode *pcp)
510 {
511 	struct buf *bp;
512 	int error;
513 	struct vnode *vp;
514 	struct pcfs *fsp;
515 
516 	vp = PCTOV(pcp);
517 	fsp = VFSTOPCFS(vp->v_vfsp);
518 	if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
519 		/* no node to update */
520 		pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
521 		return (0);
522 	}
523 	if (vp->v_flag & VROOT) {
524 		panic("pc_nodeupdate");
525 	}
526 	if (pcp->pc_flags & PC_INVAL)
527 		return (0);
528 	PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp,
529 	    pcp->pc_eblkno, pcp->pc_eoffset);
530 
531 	if (error = pc_getentryblock(pcp, &bp)) {
532 		return (error);
533 	}
534 	if (vp->v_type == VREG) {
535 		if (pcp->pc_flags & PC_CHG)
536 			pcp->pc_entry.pcd_attr |= PCA_ARCH;
537 		pcp->pc_entry.pcd_size = htoli(pcp->pc_size);
538 	}
539 	pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster);
540 	*((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry;
541 	bwrite2(bp);
542 	error = geterror(bp);
543 	if (error)
544 		error = EIO;
545 	brelse(bp);
546 	if (error) {
547 		PC_DPRINTF0(1, "pc_nodeupdate ERROR\n");
548 		pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
549 	}
550 	pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
551 	return (error);
552 }
553 
554 /*
555  * Verify that the disk in the drive is the same one that we
556  * got the pcnode from.
557  * MUST be called with node unlocked.
558  */
559 /* ARGSUSED */
560 int
561 pc_verify(struct pcfs *fsp)
562 {
563 	int fdstatus = 0;
564 	int error = 0;
565 
566 	if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV)
567 		return (EIO);
568 
569 	if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) {
570 		PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp);
571 		error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev,
572 		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE|FKIOCTL,
573 		    NULL, NULL);
574 
575 		if (error) {
576 			if (error == ENOTTY || error == ENXIO) {
577 				error = 0;
578 			} else {
579 				PC_DPRINTF1(1,
580 				    "pc_verify: FDGETCHANGE ioctl failed: %d\n",
581 				    error);
582 				pc_mark_irrecov(fsp);
583 			}
584 		} else if (fsp->pcfs_fatjustread) {
585 			/*
586 			 * Ignore the results of the ioctl if we just
587 			 * read the FAT.  There is a good chance that
588 			 * the disk changed bit will be on, because
589 			 * we've just mounted and we don't want to
590 			 * give a false positive that the sky is falling.
591 			 */
592 			fsp->pcfs_fatjustread = 0;
593 		} else {
594 			/*
595 			 * Oddly enough we can't check just one flag here. The
596 			 * x86 floppy driver sets a different flag
597 			 * (FDGC_DETECTED) than the sparc driver does.
598 			 * I think this MAY be a bug, and I filed 4165938
599 			 * to get someone to look at the behavior
600 			 * a bit more closely.  In the meantime, my testing and
601 			 * code examination seem to indicate it is safe to
602 			 * check for either bit being set.
603 			 */
604 			if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) {
605 				PC_DPRINTF0(1, "pc_verify: change detected\n");
606 				pc_mark_irrecov(fsp);
607 			}
608 		}
609 	}
610 	if (!(error || fsp->pcfs_fatp)) {
611 		error = pc_getfat(fsp);
612 	}
613 
614 	return (error);
615 }
616 
617 /*
618  * The disk has changed, pulling the rug out from beneath us.
619  * Mark the FS as being in an irrecoverable state.
620  * In a short while we'll clean up.
621  */
622 void
623 pc_mark_irrecov(struct pcfs *fsp)
624 {
625 	if (!(fsp->pcfs_flags & PCFS_NOCHK)) {
626 		if (pc_lockfs(fsp, 1, 0)) {
627 			/*
628 			 * Locking failed, which currently would
629 			 * only happen if the FS were already
630 			 * marked as hosed.  If another reason for
631 			 * failure were to arise in the future, this
632 			 * routine would have to change.
633 			 */
634 			return;
635 		}
636 
637 		fsp->pcfs_flags |= PCFS_IRRECOV;
638 		cmn_err(CE_WARN,
639 			"Disk was changed during an update or\n"
640 			"an irrecoverable error was encountered.\n"
641 			"File damage is possible.  To prevent further\n"
642 			"damage, this pcfs instance will now be frozen.\n"
643 			"Use umount(1M) to release the instance.\n");
644 		(void) pc_unlockfs(fsp);
645 	}
646 }
647 
648 /*
649  * The disk has been changed!
650  */
651 void
652 pc_diskchanged(struct pcfs *fsp)
653 {
654 	struct pcnode	*pcp, *npcp = NULL;
655 	struct pchead	*hp;
656 	struct vnode	*vp;
657 	extern vfs_t	EIO_vfs;
658 	struct vfs	*vfsp;
659 
660 	/*
661 	 * Eliminate all pcnodes (dir & file) associated with this fs.
662 	 * If the node is internal, ie, no references outside of
663 	 * pcfs itself, then release the associated vnode structure.
664 	 * Invalidate the in core FAT.
665 	 * Invalidate cached data blocks and blocks waiting for I/O.
666 	 */
667 	PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp);
668 
669 	vfsp = PCFSTOVFS(fsp);
670 
671 	for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) {
672 		for (pcp = hp->pch_forw;
673 		    pcp != (struct pcnode *)hp; pcp = npcp) {
674 			npcp = pcp -> pc_forw;
675 			vp = PCTOV(pcp);
676 			if ((vp->v_vfsp == vfsp) &&
677 			    !(pcp->pc_flags & PC_RELEHOLD)) {
678 				mutex_enter(&(vp)->v_lock);
679 				if (vp->v_count > 0) {
680 					mutex_exit(&(vp)->v_lock);
681 					continue;
682 				}
683 				mutex_exit(&(vp)->v_lock);
684 				VN_HOLD(vp);
685 				remque(pcp);
686 				vp->v_data = NULL;
687 				vp->v_vfsp = &EIO_vfs;
688 				vp->v_type = VBAD;
689 				VN_RELE(vp);
690 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
691 					(void) pvn_vplist_dirty(vp,
692 					    (u_offset_t)0, pcfs_putapage,
693 					    B_INVAL | B_TRUNC,
694 					    (struct cred *)NULL);
695 					vn_free(vp);
696 				}
697 				kmem_free(pcp, sizeof (struct pcnode));
698 				fsp->pcfs_nrefs --;
699 				VFS_RELE(vfsp);
700 			}
701 		}
702 	}
703 	for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) {
704 		for (pcp = hp->pch_forw; fsp->pcfs_frefs &&
705 		    pcp != (struct pcnode *)hp; pcp = npcp) {
706 			npcp = pcp -> pc_forw;
707 			vp = PCTOV(pcp);
708 			if ((vp->v_vfsp == vfsp) &&
709 			    !(pcp->pc_flags & PC_RELEHOLD)) {
710 				mutex_enter(&(vp)->v_lock);
711 				if (vp->v_count > 0) {
712 					mutex_exit(&(vp)->v_lock);
713 					continue;
714 				}
715 				mutex_exit(&(vp)->v_lock);
716 				VN_HOLD(vp);
717 				remque(pcp);
718 				vp->v_data = NULL;
719 				vp->v_vfsp = &EIO_vfs;
720 				vp->v_type = VBAD;
721 				VN_RELE(vp);
722 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
723 					(void) pvn_vplist_dirty(vp,
724 					    (u_offset_t)0, pcfs_putapage,
725 					    B_INVAL | B_TRUNC,
726 					    (struct cred *)NULL);
727 					vn_free(vp);
728 				}
729 				kmem_free(pcp, sizeof (struct pcnode));
730 				fsp->pcfs_frefs--;
731 				fsp->pcfs_nrefs--;
732 				VFS_RELE(vfsp);
733 			}
734 		}
735 	}
736 #ifdef undef
737 	if (fsp->pcfs_frefs) {
738 		rw_exit(&pcnodes_lock);
739 		panic("pc_diskchanged: frefs");
740 	}
741 	if (fsp->pcfs_nrefs) {
742 		rw_exit(&pcnodes_lock);
743 		panic("pc_diskchanged: nrefs");
744 	}
745 #endif
746 	if (!(vfsp->vfs_flag & VFS_UNMOUNTED) &&
747 	    fsp->pcfs_fatp != (uchar_t *)0) {
748 		pc_invalfat(fsp);
749 	} else {
750 		binval(fsp->pcfs_xdev);
751 	}
752 }
753