xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_node.c (revision 446d25c9e29c018a681c467da83afd71d02b647b)
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  * Copyright (c) 2017 by Delphix. All rights reserved.
25  * Copyright 2024 MNX Cloud, Inc.
26  */
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
pc_init(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 *
pc_getnode(struct pcfs * fsp,daddr_t blkno,int offset,struct pcdir * ep)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 == NULL) {
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_zalloc(sizeof (struct pcnode), KM_SLEEP);
144 	vp = vn_alloc(KM_SLEEP);
145 	pcp->pc_vn = vp;
146 	pcp->pc_entry = *ep;
147 	pcp->pc_eblkno = blkno;
148 	pcp->pc_eoffset = offset;
149 	pcp->pc_scluster = scluster;
150 	pcp->pc_lcluster = scluster;
151 	pcp->pc_lindex = 0;
152 	pcp->pc_flags = 0;
153 	if (ep->pcd_attr & PCA_DIR) {
154 		vn_setops(vp, pcfs_dvnodeops);
155 		vp->v_type = VDIR;
156 		if (scluster == 0) {
157 			vp->v_flag = VROOT;
158 			blkno = offset = 0;
159 			if (IS_FAT32(fsp)) {
160 				pc_cluster32_t ncl = 0;
161 
162 				scluster = fsp->pcfs_rdirstart;
163 				if (pc_fileclsize(fsp, scluster, &ncl)) {
164 					PC_DPRINTF1(2, "cluster chain "
165 					    "corruption, scluster=%d\n",
166 					    scluster);
167 					pcp->pc_flags |= PC_INVAL;
168 				}
169 				pcp->pc_size = fsp->pcfs_clsize * ncl;
170 			} else {
171 				pcp->pc_size =
172 				    fsp->pcfs_rdirsec * fsp->pcfs_secsize;
173 			}
174 		} else {
175 			pc_cluster32_t ncl = 0;
176 
177 			if (pc_fileclsize(fsp, scluster, &ncl)) {
178 				PC_DPRINTF1(2, "cluster chain corruption, "
179 				    "scluster=%d\n", scluster);
180 				pcp->pc_flags |= PC_INVAL;
181 			}
182 			pcp->pc_size = fsp->pcfs_clsize * ncl;
183 		}
184 	} else {
185 		vn_setops(vp, pcfs_fvnodeops);
186 		vp->v_type = VREG;
187 		vp->v_flag = VNOSWAP;
188 		fsp->pcfs_frefs++;
189 		pcp->pc_size = ltohi(ep->pcd_size);
190 	}
191 	fsp->pcfs_nrefs++;
192 	VFS_HOLD(PCFSTOVFS(fsp));
193 	vp->v_data = (caddr_t)pcp;
194 	vp->v_vfsp = PCFSTOVFS(fsp);
195 	vn_exists(vp);
196 	rw_enter(&pcnodes_lock, RW_WRITER);
197 	insque(pcp, hp);
198 	rw_exit(&pcnodes_lock);
199 	return (pcp);
200 }
201 
202 int
syncpcp(struct pcnode * pcp,int flags)203 syncpcp(struct pcnode *pcp, int flags)
204 {
205 	int err;
206 	if (!vn_has_cached_data(PCTOV(pcp)))
207 		err = 0;
208 	else
209 		err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags,
210 		    kcred, NULL);
211 
212 	return (err);
213 }
214 
215 void
pc_rele(struct pcnode * pcp)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 	VN_RELE_LOCKED(vp);
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 /* ARGSUSED */
304 void
pc_mark_mod(struct pcfs * fsp,struct pcnode * pcp)305 pc_mark_mod(struct pcfs *fsp, struct pcnode *pcp)
306 {
307 	timestruc_t now;
308 
309 	if (PCTOV(pcp)->v_type == VDIR)
310 		return;
311 
312 	ASSERT(PCTOV(pcp)->v_type == VREG);
313 
314 	gethrestime(&now);
315 	if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
316 		PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
317 		    "conversion, curtime = %lld\n",
318 		    (long long)now.tv_sec);
319 
320 	pcp->pc_flags |= PC_CHG;
321 }
322 
323 /*
324  * Mark a pcnode as accessed with the current time.
325  */
326 void
pc_mark_acc(struct pcfs * fsp,struct pcnode * pcp)327 pc_mark_acc(struct pcfs *fsp, struct pcnode *pcp)
328 {
329 	struct pctime pt = { 0, 0 };
330 	timestruc_t now;
331 
332 	if (fsp->pcfs_flags & PCFS_NOATIME || PCTOV(pcp)->v_type == VDIR)
333 		return;
334 
335 	ASSERT(PCTOV(pcp)->v_type == VREG);
336 
337 	gethrestime(&now);
338 	if (pc_tvtopct(&now, &pt)) {
339 		PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
340 		    "conversion, curtime = %lld\n",
341 		    (long long)now.tv_sec);
342 		return;
343 	}
344 
345 	/*
346 	 * We don't really want to write the adate for every access
347 	 * on flash media; make sure it really changed !
348 	 */
349 	if (pcp->pc_entry.pcd_ladate != pt.pct_date) {
350 		pcp->pc_entry.pcd_ladate = pt.pct_date;
351 		pcp->pc_flags |= (PC_CHG | PC_ACC);
352 	}
353 }
354 
355 /*
356  * Truncate a file to a length.
357  * Node must be locked.
358  */
359 int
pc_truncate(struct pcnode * pcp,uint_t length)360 pc_truncate(struct pcnode *pcp, uint_t length)
361 {
362 	struct pcfs *fsp;
363 	struct vnode *vp;
364 	int error = 0;
365 
366 	PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n",
367 	    (void *)pcp, length, pcp->pc_size);
368 	vp = PCTOV(pcp);
369 	if (pcp->pc_flags & PC_INVAL)
370 		return (EIO);
371 	fsp = VFSTOPCFS(vp->v_vfsp);
372 	/*
373 	 * directories are always truncated to zero and are not marked
374 	 */
375 	if (vp->v_type == VDIR) {
376 		error = pc_bfree(pcp, 0);
377 		return (error);
378 	}
379 	/*
380 	 * If length is the same as the current size
381 	 * just mark the pcnode and return.
382 	 */
383 	if (length > pcp->pc_size) {
384 		daddr_t bno;
385 		uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize);
386 
387 		/*
388 		 * We are extending a file.
389 		 * Extend it with _one_ call to pc_balloc (no holes)
390 		 * since we don't need to use the block number(s).
391 		 */
392 		if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) <
393 		    (daddr_t)llcn) {
394 			error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno);
395 		}
396 		if (error) {
397 			pc_cluster32_t ncl = 0;
398 			PC_DPRINTF1(2, "pc_truncate: error=%d\n", error);
399 			/*
400 			 * probably ran out disk space;
401 			 * determine current file size
402 			 */
403 			if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) {
404 				PC_DPRINTF1(2, "cluster chain corruption, "
405 				    "scluster=%d\n", pcp->pc_scluster);
406 				pcp->pc_flags |= PC_INVAL;
407 			}
408 			pcp->pc_size = fsp->pcfs_clsize * ncl;
409 		} else
410 			pcp->pc_size = length;
411 
412 	} else if (length < pcp->pc_size) {
413 		/*
414 		 * We are shrinking a file.
415 		 * Free blocks after the block that length points to.
416 		 */
417 		if (pc_blkoff(fsp, length) == 0) {
418 			/*
419 			 * Truncation to a block (cluster size) boundary only
420 			 * requires us to invalidate everything after the new
421 			 * end of the file.
422 			 */
423 			(void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length,
424 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
425 		} else {
426 			/*
427 			 * pvn_vpzero() cannot deal with more than MAXBSIZE
428 			 * chunks. Since the FAT clustersize can get larger
429 			 * than that, we'll zero from the new length to the
430 			 * end of the cluster for clustersizes smaller than
431 			 * MAXBSIZE - or the end of the MAXBSIZE block in
432 			 * case we've got a large clustersize.
433 			 */
434 			size_t nbytes =
435 			    roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) -
436 			    length;
437 
438 			pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes);
439 			(void) pvn_vplist_dirty(PCTOV(pcp),
440 			    (u_offset_t)length + nbytes,
441 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
442 		}
443 		error = pc_bfree(pcp, (pc_cluster32_t)
444 		    howmany((offset_t)length, fsp->pcfs_clsize));
445 		pcp->pc_size = length;
446 	}
447 
448 	/*
449 	 * This is the only place in PCFS code where pc_mark_mod() is called
450 	 * without setting PC_MOD. May be a historical artifact ...
451 	 */
452 	pc_mark_mod(fsp, pcp);
453 	return (error);
454 }
455 
456 /*
457  * Get block for entry.
458  */
459 static int
pc_getentryblock(struct pcnode * pcp,struct buf ** bpp)460 pc_getentryblock(struct pcnode *pcp, struct buf **bpp)
461 {
462 	struct pcfs *fsp;
463 
464 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
465 	if (pcp->pc_eblkno >= fsp->pcfs_datastart ||
466 	    (pcp->pc_eblkno - fsp->pcfs_rdirstart) <
467 	    (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
468 		*bpp = bread(fsp->pcfs_xdev,
469 		    pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize);
470 	} else {
471 		*bpp = bread(fsp->pcfs_xdev,
472 		    pc_dbdaddr(fsp, pcp->pc_eblkno),
473 		    (int)(fsp->pcfs_datastart - pcp->pc_eblkno) *
474 		    fsp->pcfs_secsize);
475 	}
476 	if ((*bpp)->b_flags & B_ERROR) {
477 		brelse(*bpp);
478 		pc_mark_irrecov(fsp);
479 		return (EIO);
480 	}
481 	return (0);
482 }
483 
484 /*
485  * Sync all data associated with a file.
486  * Flush all the blocks in the buffer cache out to disk, sync the FAT and
487  * update the directory entry.
488  */
489 int
pc_nodesync(struct pcnode * pcp)490 pc_nodesync(struct pcnode *pcp)
491 {
492 	struct pcfs *fsp;
493 	int err;
494 	struct vnode *vp;
495 
496 	vp = PCTOV(pcp);
497 	fsp = VFSTOPCFS(vp->v_vfsp);
498 	err = 0;
499 	if (pcp->pc_flags & PC_MOD) {
500 		/*
501 		 * Flush all data blocks from buffer cache and
502 		 * update the FAT which points to the data.
503 		 */
504 		if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */
505 			if (err == ENOMEM)
506 				return (err);
507 			else {
508 				pc_mark_irrecov(fsp);
509 				return (EIO);
510 			}
511 		}
512 		pcp->pc_flags &= ~PC_MOD;
513 	}
514 	/*
515 	 * update the directory entry
516 	 */
517 	if (pcp->pc_flags & PC_CHG)
518 		(void) pc_nodeupdate(pcp);
519 	return (err);
520 }
521 
522 /*
523  * Update the node's directory entry.
524  */
525 int
pc_nodeupdate(struct pcnode * pcp)526 pc_nodeupdate(struct pcnode *pcp)
527 {
528 	struct buf *bp;
529 	int error;
530 	struct vnode *vp;
531 	struct pcfs *fsp;
532 
533 	vp = PCTOV(pcp);
534 	fsp = VFSTOPCFS(vp->v_vfsp);
535 	if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
536 		/* no node to update */
537 		pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
538 		return (0);
539 	}
540 	if (vp->v_flag & VROOT) {
541 		panic("pc_nodeupdate");
542 	}
543 	if (pcp->pc_flags & PC_INVAL)
544 		return (0);
545 	PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp,
546 	    pcp->pc_eblkno, pcp->pc_eoffset);
547 
548 	if (error = pc_getentryblock(pcp, &bp)) {
549 		return (error);
550 	}
551 	if (vp->v_type == VREG) {
552 		if (pcp->pc_flags & PC_CHG)
553 			pcp->pc_entry.pcd_attr |= PCA_ARCH;
554 		pcp->pc_entry.pcd_size = htoli(pcp->pc_size);
555 	}
556 	pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster);
557 	*((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry;
558 	bwrite2(bp);
559 	error = geterror(bp);
560 	brelse(bp);
561 	if (error) {
562 		error = EIO;
563 		pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
564 	}
565 	pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
566 	return (error);
567 }
568 
569 /*
570  * Verify that the disk in the drive is the same one that we
571  * got the pcnode from.
572  * MUST be called with node unlocked.
573  */
574 int
pc_verify(struct pcfs * fsp)575 pc_verify(struct pcfs *fsp)
576 {
577 	int fdstatus = 0;
578 	int error = 0;
579 
580 	if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV)
581 		return (EIO);
582 
583 	if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) {
584 		/*
585 		 * This "has it been removed" check should better be
586 		 * modified for removeable media that are not floppies.
587 		 * dkio-managed devices such as USB/firewire external
588 		 * disks/memory sticks/floppies (gasp) do not understand
589 		 * this ioctl.
590 		 */
591 		PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp);
592 		error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev,
593 		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE | FKIOCTL,
594 		    NULL, NULL);
595 
596 		if (error) {
597 			if (error == ENOTTY || error == ENXIO) {
598 				/*
599 				 * See comment above. This is a workaround
600 				 * for removeable media that don't understand
601 				 * floppy ioctls.
602 				 */
603 				error = 0;
604 			} else {
605 				PC_DPRINTF1(1,
606 				    "pc_verify: FDGETCHANGE ioctl failed: %d\n",
607 				    error);
608 				pc_mark_irrecov(fsp);
609 			}
610 		} else if (fsp->pcfs_fatjustread) {
611 			/*
612 			 * Ignore the results of the ioctl if we just
613 			 * read the FAT.  There is a good chance that
614 			 * the disk changed bit will be on, because
615 			 * we've just mounted and we don't want to
616 			 * give a false positive that the sky is falling.
617 			 */
618 			fsp->pcfs_fatjustread = 0;
619 		} else {
620 			/*
621 			 * Oddly enough we can't check just one flag here. The
622 			 * x86 floppy driver sets a different flag
623 			 * (FDGC_DETECTED) than the sparc driver does.
624 			 * I think this MAY be a bug, and I filed 4165938
625 			 * to get someone to look at the behavior
626 			 * a bit more closely.  In the meantime, my testing and
627 			 * code examination seem to indicate it is safe to
628 			 * check for either bit being set.
629 			 */
630 			if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) {
631 				PC_DPRINTF0(1, "pc_verify: change detected\n");
632 				pc_mark_irrecov(fsp);
633 			}
634 		}
635 	}
636 	if (error == 0 && fsp->pcfs_fatp == NULL) {
637 		error = pc_getfat(fsp);
638 	}
639 
640 	return (error);
641 }
642 
643 /*
644  * The disk has changed, pulling the rug out from beneath us.
645  * Mark the FS as being in an irrecoverable state.
646  * In a short while we'll clean up.
647  */
648 void
pc_mark_irrecov(struct pcfs * fsp)649 pc_mark_irrecov(struct pcfs *fsp)
650 {
651 	if (!(fsp->pcfs_flags & PCFS_NOCHK)) {
652 		if (pc_lockfs(fsp, 1, 0)) {
653 			/*
654 			 * Locking failed, which currently would
655 			 * only happen if the FS were already
656 			 * marked as hosed.  If another reason for
657 			 * failure were to arise in the future, this
658 			 * routine would have to change.
659 			 */
660 			return;
661 		}
662 
663 		fsp->pcfs_flags |= PCFS_IRRECOV;
664 		cmn_err(CE_WARN,
665 		    "Disk was changed during an update or\n"
666 		    "an irrecoverable error was encountered.\n"
667 		    "File damage is possible.  To prevent further\n"
668 		    "damage, this pcfs instance will now be frozen.\n"
669 		    "Use umount(8) to release the instance.\n");
670 		(void) pc_unlockfs(fsp);
671 	}
672 }
673 
674 /*
675  * The disk has been changed!
676  */
677 void
pc_diskchanged(struct pcfs * fsp)678 pc_diskchanged(struct pcfs *fsp)
679 {
680 	struct pcnode	*pcp, *npcp = NULL;
681 	struct pchead	*hp;
682 	struct vnode	*vp;
683 	extern vfs_t	EIO_vfs;
684 	struct vfs	*vfsp;
685 
686 	/*
687 	 * Eliminate all pcnodes (dir & file) associated with this fs.
688 	 * If the node is internal, ie, no references outside of
689 	 * pcfs itself, then release the associated vnode structure.
690 	 * Invalidate the in core FAT.
691 	 * Invalidate cached data blocks and blocks waiting for I/O.
692 	 */
693 	PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp);
694 
695 	vfsp = PCFSTOVFS(fsp);
696 
697 	for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) {
698 		for (pcp = hp->pch_forw;
699 		    pcp != (struct pcnode *)hp; pcp = npcp) {
700 			npcp = pcp -> pc_forw;
701 			vp = PCTOV(pcp);
702 			if ((vp->v_vfsp == vfsp) &&
703 			    !(pcp->pc_flags & PC_RELEHOLD)) {
704 				mutex_enter(&(vp)->v_lock);
705 				if (vp->v_count > 0) {
706 					mutex_exit(&(vp)->v_lock);
707 					continue;
708 				}
709 				mutex_exit(&(vp)->v_lock);
710 				VN_HOLD(vp);
711 				remque(pcp);
712 				vp->v_data = NULL;
713 				vp->v_vfsp = &EIO_vfs;
714 				vp->v_type = VBAD;
715 				VN_RELE(vp);
716 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
717 					(void) pvn_vplist_dirty(vp,
718 					    (u_offset_t)0, pcfs_putapage,
719 					    B_INVAL | B_TRUNC,
720 					    (struct cred *)NULL);
721 					vn_free(vp);
722 				}
723 				kmem_free(pcp, sizeof (struct pcnode));
724 				fsp->pcfs_nrefs --;
725 				VFS_RELE(vfsp);
726 			}
727 		}
728 	}
729 	for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) {
730 		for (pcp = hp->pch_forw; fsp->pcfs_frefs &&
731 		    pcp != (struct pcnode *)hp; pcp = npcp) {
732 			npcp = pcp -> pc_forw;
733 			vp = PCTOV(pcp);
734 			if ((vp->v_vfsp == vfsp) &&
735 			    !(pcp->pc_flags & PC_RELEHOLD)) {
736 				mutex_enter(&(vp)->v_lock);
737 				if (vp->v_count > 0) {
738 					mutex_exit(&(vp)->v_lock);
739 					continue;
740 				}
741 				mutex_exit(&(vp)->v_lock);
742 				VN_HOLD(vp);
743 				remque(pcp);
744 				vp->v_data = NULL;
745 				vp->v_vfsp = &EIO_vfs;
746 				vp->v_type = VBAD;
747 				VN_RELE(vp);
748 				if (!(pcp->pc_flags & PC_EXTERNAL)) {
749 					(void) pvn_vplist_dirty(vp,
750 					    (u_offset_t)0, pcfs_putapage,
751 					    B_INVAL | B_TRUNC,
752 					    (struct cred *)NULL);
753 					vn_free(vp);
754 				}
755 				kmem_free(pcp, sizeof (struct pcnode));
756 				fsp->pcfs_frefs--;
757 				fsp->pcfs_nrefs--;
758 				VFS_RELE(vfsp);
759 			}
760 		}
761 	}
762 #ifdef undef
763 	if (fsp->pcfs_frefs) {
764 		rw_exit(&pcnodes_lock);
765 		panic("pc_diskchanged: frefs");
766 	}
767 	if (fsp->pcfs_nrefs) {
768 		rw_exit(&pcnodes_lock);
769 		panic("pc_diskchanged: nrefs");
770 	}
771 #endif
772 	if (!(vfsp->vfs_flag & VFS_UNMOUNTED) &&
773 	    fsp->pcfs_fatp != NULL) {
774 		pc_invalfat(fsp);
775 	} else {
776 		binval(fsp->pcfs_xdev);
777 	}
778 }
779