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
pc_init(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 *
pc_getnode(struct pcfs * fsp,daddr_t blkno,int offset,struct pcdir * ep)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
syncpcp(struct pcnode * pcp,int flags)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
pc_rele(struct pcnode * pcp)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
pc_mark_mod(struct pcfs * fsp,struct pcnode * pcp)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
pc_mark_acc(struct pcfs * fsp,struct pcnode * pcp)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
pc_truncate(struct pcnode * pcp,uint_t length)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
pc_getentryblock(struct pcnode * pcp,struct buf ** bpp)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
pc_nodesync(struct pcnode * pcp)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
pc_nodeupdate(struct pcnode * pcp)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
pc_verify(struct pcfs * fsp)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
pc_mark_irrecov(struct pcfs * fsp)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
pc_diskchanged(struct pcfs * fsp)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