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