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