xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_alloc.c (revision 32d46495)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5264a6e74Sfrankho  * Common Development and Distribution License (the "License").
6264a6e74Sfrankho  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*32d46495SMichael Bergknoff  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Routines to allocate and deallocate data blocks on the disk
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/errno.h>
327c478bd9Sstevel@tonic-gate #include <sys/buf.h>
337c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
347c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
357c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
367c478bd9Sstevel@tonic-gate #include <sys/debug.h>
377c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
387c478bd9Sstevel@tonic-gate #include <sys/systm.h>
397c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
417c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
427c478bd9Sstevel@tonic-gate #include <sys/fs/pc_node.h>
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate static pc_cluster32_t pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn);
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /*
477c478bd9Sstevel@tonic-gate  * Convert file logical block (cluster) numbers to disk block numbers.
487c478bd9Sstevel@tonic-gate  * Also return number of physically contiguous blocks if asked for.
497c478bd9Sstevel@tonic-gate  * Used for reading only. Use pc_balloc for writing.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate int
pc_bmap(struct pcnode * pcp,daddr_t lcn,daddr_t * dbnp,uint_t * contigbp)527c478bd9Sstevel@tonic-gate pc_bmap(
537c478bd9Sstevel@tonic-gate 	struct pcnode *pcp,		/* pcnode for file */
547c478bd9Sstevel@tonic-gate 	daddr_t lcn,			/* logical cluster no */
557c478bd9Sstevel@tonic-gate 	daddr_t *dbnp,			/* ptr to phys block no */
567c478bd9Sstevel@tonic-gate 	uint_t *contigbp)		/* ptr to number of contiguous bytes */
577c478bd9Sstevel@tonic-gate 					/* may be zero if not wanted */
587c478bd9Sstevel@tonic-gate {
597c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;	/* pcfs that file is in */
607c478bd9Sstevel@tonic-gate 	struct vnode *vp;
617c478bd9Sstevel@tonic-gate 	pc_cluster32_t cn, ncn;		/* current, next cluster number */
627c478bd9Sstevel@tonic-gate 	daddr_t olcn = lcn;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
657c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
66f127cb91Sfrankho 
677c478bd9Sstevel@tonic-gate 	if (lcn < 0)
687c478bd9Sstevel@tonic-gate 		return (ENOENT);
69f127cb91Sfrankho 
70f127cb91Sfrankho 	/*
71f127cb91Sfrankho 	 * FAT12 / FAT16 root directories are a continuous section on disk
72f127cb91Sfrankho 	 * before the actual data clusters. Specialcase this here.
73f127cb91Sfrankho 	 */
747c478bd9Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
75f127cb91Sfrankho 		daddr_t lbn; /* logical (disk) block number */
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 		lbn = pc_cltodb(fsp, lcn);
787c478bd9Sstevel@tonic-gate 		if (lbn >= fsp->pcfs_rdirsec) {
797c478bd9Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT1\n");
807c478bd9Sstevel@tonic-gate 			return (ENOENT);
817c478bd9Sstevel@tonic-gate 		}
82f127cb91Sfrankho 		*dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
837c478bd9Sstevel@tonic-gate 		if (contigbp) {
847c478bd9Sstevel@tonic-gate 			ASSERT (*contigbp >= fsp->pcfs_secsize);
857c478bd9Sstevel@tonic-gate 			*contigbp = MIN(*contigbp,
867c478bd9Sstevel@tonic-gate 			    fsp->pcfs_secsize * (fsp->pcfs_rdirsec - lbn));
877c478bd9Sstevel@tonic-gate 		}
88f127cb91Sfrankho 		return (0);
89f127cb91Sfrankho 	}
907c478bd9Sstevel@tonic-gate 
91f127cb91Sfrankho 	if (lcn >= fsp->pcfs_ncluster) {
92f127cb91Sfrankho 		PC_DPRINTF0(2, "pc_bmap: ENOENT2\n");
93f127cb91Sfrankho 		return (ENOENT);
94f127cb91Sfrankho 	}
95f127cb91Sfrankho 	if (vp->v_type == VREG &&
96f127cb91Sfrankho 	    (pcp->pc_size == 0 ||
97f127cb91Sfrankho 	    lcn >= (daddr_t)howmany((offset_t)pcp->pc_size,
98f127cb91Sfrankho 			fsp->pcfs_clsize))) {
99f127cb91Sfrankho 		PC_DPRINTF0(2, "pc_bmap: ENOENT3\n");
100f127cb91Sfrankho 		return (ENOENT);
101f127cb91Sfrankho 	}
102f127cb91Sfrankho 	ncn = pcp->pc_scluster;
103f127cb91Sfrankho 	if (IS_FAT32(fsp) && ncn == 0)
104f127cb91Sfrankho 		ncn = fsp->pcfs_rdirstart;
105f127cb91Sfrankho 
106f127cb91Sfrankho 	/* Do we have a cached index/cluster pair? */
107f127cb91Sfrankho 	if (pcp->pc_lindex > 0 && lcn >= pcp->pc_lindex) {
108f127cb91Sfrankho 		lcn -= pcp->pc_lindex;
109f127cb91Sfrankho 		ncn = pcp->pc_lcluster;
110f127cb91Sfrankho 	}
111f127cb91Sfrankho 	do {
112f127cb91Sfrankho 		cn = ncn;
113f127cb91Sfrankho 		if (!pc_validcl(fsp, cn)) {
114f127cb91Sfrankho 			if (IS_FAT32(fsp) && cn >= PCF_LASTCLUSTER32 &&
115f127cb91Sfrankho 			    vp->v_type == VDIR) {
116f127cb91Sfrankho 				PC_DPRINTF0(2, "pc_bmap: ENOENT4\n");
117f127cb91Sfrankho 				return (ENOENT);
118f127cb91Sfrankho 			} else if (!IS_FAT32(fsp) &&
119f127cb91Sfrankho 			    cn >= PCF_LASTCLUSTER &&
120f127cb91Sfrankho 			    vp->v_type == VDIR) {
121f127cb91Sfrankho 				PC_DPRINTF0(2, "pc_bmap: ENOENT5\n");
122f127cb91Sfrankho 				return (ENOENT);
123f127cb91Sfrankho 			} else {
124f127cb91Sfrankho 				PC_DPRINTF1(1,
125f127cb91Sfrankho 				    "pc_bmap: badfs cn=%d\n", cn);
126f127cb91Sfrankho 				(void) pc_badfs(fsp);
127f127cb91Sfrankho 				return (EIO);
128f127cb91Sfrankho 			}
1297c478bd9Sstevel@tonic-gate 		}
130f127cb91Sfrankho 		ncn = pc_getcluster(fsp, cn);
131f127cb91Sfrankho 	} while (lcn--);
132f127cb91Sfrankho 
133f127cb91Sfrankho 	/*
134f127cb91Sfrankho 	 * Cache this cluster, as we'll most likely visit the
135f127cb91Sfrankho 	 * one after this next time.  Considerably improves
136f127cb91Sfrankho 	 * performance on sequential reads and writes.
137f127cb91Sfrankho 	 */
138f127cb91Sfrankho 	pcp->pc_lindex = olcn;
139f127cb91Sfrankho 	pcp->pc_lcluster = cn;
140f127cb91Sfrankho 	*dbnp = pc_cldaddr(fsp, cn);
141f127cb91Sfrankho 
142f127cb91Sfrankho 	if (contigbp && *contigbp > fsp->pcfs_clsize) {
143f127cb91Sfrankho 		uint_t count = fsp->pcfs_clsize;
144f127cb91Sfrankho 
145f127cb91Sfrankho 		while ((cn + 1) == ncn && count < *contigbp &&
146f127cb91Sfrankho 		    pc_validcl(fsp, ncn)) {
147f127cb91Sfrankho 			count += fsp->pcfs_clsize;
1487c478bd9Sstevel@tonic-gate 			cn = ncn;
149f127cb91Sfrankho 			ncn = pc_getcluster(fsp, ncn);
1507c478bd9Sstevel@tonic-gate 		}
151f127cb91Sfrankho 		*contigbp = count;
1527c478bd9Sstevel@tonic-gate 	}
1537c478bd9Sstevel@tonic-gate 	return (0);
1547c478bd9Sstevel@tonic-gate }
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate /*
1577c478bd9Sstevel@tonic-gate  * Allocate file logical blocks (clusters).
1587c478bd9Sstevel@tonic-gate  * Return disk address of last allocated cluster.
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate int
pc_balloc(struct pcnode * pcp,daddr_t lcn,int zwrite,daddr_t * dbnp)1617c478bd9Sstevel@tonic-gate pc_balloc(
1627c478bd9Sstevel@tonic-gate 	struct pcnode *pcp,	/* pcnode for file */
1637c478bd9Sstevel@tonic-gate 	daddr_t lcn,		/* logical cluster no */
1647c478bd9Sstevel@tonic-gate 	int zwrite,			/* zerofill blocks? */
1657c478bd9Sstevel@tonic-gate 	daddr_t *dbnp)			/* ptr to phys block no */
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;	/* pcfs that file is in */
1687c478bd9Sstevel@tonic-gate 	struct vnode *vp;
169f127cb91Sfrankho 	pc_cluster32_t cn;	/* current cluster number */
170f127cb91Sfrankho 	pc_cluster32_t ncn;	/* next cluster number */
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
1737c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp -> v_vfsp);
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	if (lcn < 0) {
1767c478bd9Sstevel@tonic-gate 		return (EFBIG);
1777c478bd9Sstevel@tonic-gate 	}
1787c478bd9Sstevel@tonic-gate 
179f127cb91Sfrankho 	/*
180f127cb91Sfrankho 	 * Again, FAT12/FAT16 root directories are not data clusters.
181f127cb91Sfrankho 	 */
1827c478bd9Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
1837c478bd9Sstevel@tonic-gate 		daddr_t lbn;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 		lbn = pc_cltodb(fsp, lcn);
1867c478bd9Sstevel@tonic-gate 		if (lbn >= fsp->pcfs_rdirsec)
1877c478bd9Sstevel@tonic-gate 			return (ENOSPC);
1887c478bd9Sstevel@tonic-gate 		*dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
189f127cb91Sfrankho 		return (0);
190f127cb91Sfrankho 	}
1917c478bd9Sstevel@tonic-gate 
192f127cb91Sfrankho 	if (lcn >= fsp->pcfs_ncluster)
193f127cb91Sfrankho 		return (ENOSPC);
194f127cb91Sfrankho 	if ((vp->v_type == VREG && pcp->pc_size == 0) ||
195f127cb91Sfrankho 	    (vp->v_type == VDIR && lcn == 0)) {
196f127cb91Sfrankho 		switch (cn = pc_alloccluster(fsp, 1)) {
197f127cb91Sfrankho 		case PCF_FREECLUSTER:
1987c478bd9Sstevel@tonic-gate 			return (ENOSPC);
199f127cb91Sfrankho 		case PCF_ERRORCLUSTER:
200f127cb91Sfrankho 			return (EIO);
201f127cb91Sfrankho 		}
202f127cb91Sfrankho 		pcp->pc_scluster = cn;
203f127cb91Sfrankho 	} else {
204f127cb91Sfrankho 		cn = pcp->pc_scluster;
205f127cb91Sfrankho 		if (IS_FAT32(fsp) && cn == 0)
206f127cb91Sfrankho 			cn = fsp->pcfs_rdirstart;
207f127cb91Sfrankho 		if (!pc_validcl(fsp, cn)) {
208f127cb91Sfrankho 			PC_DPRINTF1(1, "pc_balloc: badfs cn=%d\n", cn);
209f127cb91Sfrankho 			(void) pc_badfs(fsp);
210f127cb91Sfrankho 			return (EIO);
211f127cb91Sfrankho 		}
212f127cb91Sfrankho 	}
213f127cb91Sfrankho 
214f127cb91Sfrankho 	if (pcp->pc_lindex > 0 && lcn > pcp->pc_lindex) {
215f127cb91Sfrankho 		lcn -= pcp->pc_lindex;
216f127cb91Sfrankho 		cn = pcp->pc_lcluster;
217f127cb91Sfrankho 	}
218f127cb91Sfrankho 	while (lcn-- > 0) {
219f127cb91Sfrankho 		ncn = pc_getcluster(fsp, cn);
220f127cb91Sfrankho 		if ((IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32) ||
221f127cb91Sfrankho 		    (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER)) {
222f127cb91Sfrankho 			/*
223f127cb91Sfrankho 			 * Extend file (no holes).
224f127cb91Sfrankho 			 */
225f127cb91Sfrankho 			switch (ncn = pc_alloccluster(fsp, zwrite)) {
2267c478bd9Sstevel@tonic-gate 			case PCF_FREECLUSTER:
2277c478bd9Sstevel@tonic-gate 				return (ENOSPC);
2287c478bd9Sstevel@tonic-gate 			case PCF_ERRORCLUSTER:
2297c478bd9Sstevel@tonic-gate 				return (EIO);
2307c478bd9Sstevel@tonic-gate 			}
231f127cb91Sfrankho 			pc_setcluster(fsp, cn, ncn);
232f127cb91Sfrankho 		} else if (!pc_validcl(fsp, ncn)) {
233f127cb91Sfrankho 			PC_DPRINTF1(1,
234f127cb91Sfrankho 			    "pc_balloc: badfs ncn=%d\n", ncn);
235f127cb91Sfrankho 			(void) pc_badfs(fsp);
236f127cb91Sfrankho 			return (EIO);
2377c478bd9Sstevel@tonic-gate 		}
238f127cb91Sfrankho 		cn = ncn;
2397c478bd9Sstevel@tonic-gate 	}
240f127cb91Sfrankho 	/*
241f127cb91Sfrankho 	 * Do not cache the new cluster/index values; when
242f127cb91Sfrankho 	 * extending the file we're interested in the last
243f127cb91Sfrankho 	 * written cluster and not the last cluster allocated.
244f127cb91Sfrankho 	 */
245f127cb91Sfrankho 	*dbnp = pc_cldaddr(fsp, cn);
246f127cb91Sfrankho 
2477c478bd9Sstevel@tonic-gate 	return (0);
2487c478bd9Sstevel@tonic-gate }
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate /*
2517c478bd9Sstevel@tonic-gate  * Free file cluster chain after the first skipcl clusters.
2527c478bd9Sstevel@tonic-gate  */
2537c478bd9Sstevel@tonic-gate int
pc_bfree(struct pcnode * pcp,pc_cluster32_t skipcl)2547c478bd9Sstevel@tonic-gate pc_bfree(struct pcnode *pcp, pc_cluster32_t skipcl)
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate 	struct pcfs *fsp;
2577c478bd9Sstevel@tonic-gate 	pc_cluster32_t cn;
2587c478bd9Sstevel@tonic-gate 	pc_cluster32_t ncn;
2597c478bd9Sstevel@tonic-gate 	int n;
2607c478bd9Sstevel@tonic-gate 	struct vnode *vp;
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	vp = PCTOV(pcp);
2637c478bd9Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2647c478bd9Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
2657c478bd9Sstevel@tonic-gate 		panic("pc_bfree");
2667c478bd9Sstevel@tonic-gate 	}
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	if (pcp->pc_size == 0 && vp->v_type == VREG) {
2697c478bd9Sstevel@tonic-gate 		return (0);
2707c478bd9Sstevel@tonic-gate 	}
2717c478bd9Sstevel@tonic-gate 	if (vp->v_type == VREG) {
2727c478bd9Sstevel@tonic-gate 		n = (int)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize);
2737c478bd9Sstevel@tonic-gate 		if (n > fsp->pcfs_ncluster) {
2747c478bd9Sstevel@tonic-gate 			PC_DPRINTF1(1, "pc_bfree: badfs n=%d\n", n);
2757c478bd9Sstevel@tonic-gate 			(void) pc_badfs(fsp);
2767c478bd9Sstevel@tonic-gate 			return (EIO);
2777c478bd9Sstevel@tonic-gate 		}
2787c478bd9Sstevel@tonic-gate 	} else {
2797c478bd9Sstevel@tonic-gate 		n = fsp->pcfs_ncluster;
2807c478bd9Sstevel@tonic-gate 	}
2817c478bd9Sstevel@tonic-gate 	cn = pcp->pc_scluster;
2827c478bd9Sstevel@tonic-gate 	if (IS_FAT32(fsp) && cn == 0)
2837c478bd9Sstevel@tonic-gate 		cn = fsp->pcfs_rdirstart;
2847c478bd9Sstevel@tonic-gate 	if (skipcl == 0) {
2857c478bd9Sstevel@tonic-gate 		if (IS_FAT32(fsp))
2867c478bd9Sstevel@tonic-gate 			pcp->pc_scluster = PCF_LASTCLUSTERMARK32;
2877c478bd9Sstevel@tonic-gate 		else
2887c478bd9Sstevel@tonic-gate 			pcp->pc_scluster = PCF_LASTCLUSTERMARK;
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	/* Invalidate last used cluster cache */
2927c478bd9Sstevel@tonic-gate 	pcp->pc_lindex = 0;
2937c478bd9Sstevel@tonic-gate 	pcp->pc_lcluster = pcp->pc_scluster;
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	while (n--) {
2967c478bd9Sstevel@tonic-gate 		if (!pc_validcl(fsp, cn)) {
2977c478bd9Sstevel@tonic-gate 			PC_DPRINTF1(1, "pc_bfree: badfs cn=%d\n", cn);
2987c478bd9Sstevel@tonic-gate 			(void) pc_badfs(fsp);
2997c478bd9Sstevel@tonic-gate 			return (EIO);
3007c478bd9Sstevel@tonic-gate 		}
3017c478bd9Sstevel@tonic-gate 		ncn = pc_getcluster(fsp, cn);
3027c478bd9Sstevel@tonic-gate 		if (skipcl == 0) {
3037c478bd9Sstevel@tonic-gate 			pc_setcluster(fsp, cn, PCF_FREECLUSTER);
3047c478bd9Sstevel@tonic-gate 		} else {
3057c478bd9Sstevel@tonic-gate 			skipcl--;
3067c478bd9Sstevel@tonic-gate 			if (skipcl == 0) {
3077c478bd9Sstevel@tonic-gate 				if (IS_FAT32(fsp)) {
3087c478bd9Sstevel@tonic-gate 					pc_setcluster(fsp, cn,
3097c478bd9Sstevel@tonic-gate 					    PCF_LASTCLUSTERMARK32);
3107c478bd9Sstevel@tonic-gate 				} else
3117c478bd9Sstevel@tonic-gate 					pc_setcluster(fsp, cn,
3127c478bd9Sstevel@tonic-gate 					    PCF_LASTCLUSTERMARK);
3137c478bd9Sstevel@tonic-gate 			}
3147c478bd9Sstevel@tonic-gate 		}
3157c478bd9Sstevel@tonic-gate 		if (IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32 &&
3167c478bd9Sstevel@tonic-gate 		    vp->v_type == VDIR)
3177c478bd9Sstevel@tonic-gate 			break;
3187c478bd9Sstevel@tonic-gate 		if (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER &&
3197c478bd9Sstevel@tonic-gate 		    vp->v_type == VDIR)
3207c478bd9Sstevel@tonic-gate 			break;
3217c478bd9Sstevel@tonic-gate 		cn = ncn;
3227c478bd9Sstevel@tonic-gate 	}
3237c478bd9Sstevel@tonic-gate 	return (0);
3247c478bd9Sstevel@tonic-gate }
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate /*
3277c478bd9Sstevel@tonic-gate  * Return the number of free blocks in the filesystem.
3287c478bd9Sstevel@tonic-gate  */
3297c478bd9Sstevel@tonic-gate int
pc_freeclusters(struct pcfs * fsp)3307c478bd9Sstevel@tonic-gate pc_freeclusters(struct pcfs *fsp)
3317c478bd9Sstevel@tonic-gate {
3327c478bd9Sstevel@tonic-gate 	pc_cluster32_t cn;
333264a6e74Sfrankho 	int free = 0;
334264a6e74Sfrankho 
335264a6e74Sfrankho 	if (IS_FAT32(fsp) &&
336f127cb91Sfrankho 	    fsp->pcfs_fsinfo.fs_free_clusters != FSINFO_UNKNOWN)
337f127cb91Sfrankho 		return (fsp->pcfs_fsinfo.fs_free_clusters);
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	/*
3407c478bd9Sstevel@tonic-gate 	 * make sure the FAT is in core
3417c478bd9Sstevel@tonic-gate 	 */
342*32d46495SMichael Bergknoff 	for (cn = PCF_FIRSTCLUSTER; pc_validcl(fsp, cn); cn++) {
3437c478bd9Sstevel@tonic-gate 		if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
3447c478bd9Sstevel@tonic-gate 			free++;
3457c478bd9Sstevel@tonic-gate 		}
3467c478bd9Sstevel@tonic-gate 	}
347264a6e74Sfrankho 
348264a6e74Sfrankho 	if (IS_FAT32(fsp)) {
349f127cb91Sfrankho 		ASSERT(fsp->pcfs_fsinfo.fs_free_clusters == FSINFO_UNKNOWN);
350f127cb91Sfrankho 		fsp->pcfs_fsinfo.fs_free_clusters = free;
351264a6e74Sfrankho 	}
3527c478bd9Sstevel@tonic-gate 	return (free);
3537c478bd9Sstevel@tonic-gate }
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate /*
3567c478bd9Sstevel@tonic-gate  * Cluster manipulation routines.
3577c478bd9Sstevel@tonic-gate  * FAT must be resident.
3587c478bd9Sstevel@tonic-gate  */
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate /*
3617c478bd9Sstevel@tonic-gate  * Get the next cluster in the file cluster chain.
3627c478bd9Sstevel@tonic-gate  *	cn = current cluster number in chain
3637c478bd9Sstevel@tonic-gate  */
3647c478bd9Sstevel@tonic-gate static pc_cluster32_t
pc_getcluster(struct pcfs * fsp,pc_cluster32_t cn)3657c478bd9Sstevel@tonic-gate pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn)
3667c478bd9Sstevel@tonic-gate {
3677c478bd9Sstevel@tonic-gate 	unsigned char *fp;
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
3707c478bd9Sstevel@tonic-gate 		panic("pc_getcluster");
3717c478bd9Sstevel@tonic-gate 
372f127cb91Sfrankho 	switch (fsp->pcfs_fattype) {
373f127cb91Sfrankho 	case FAT32:
3747c478bd9Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 2);
3757c478bd9Sstevel@tonic-gate 		cn = ltohi(*(pc_cluster32_t *)fp);
376f127cb91Sfrankho 		break;
377f127cb91Sfrankho 	case FAT16:
3787c478bd9Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 1);
3797c478bd9Sstevel@tonic-gate 		cn = ltohs(*(pc_cluster16_t *)fp);
380f127cb91Sfrankho 		break;
381f127cb91Sfrankho 	case FAT12:
3827c478bd9Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
3837c478bd9Sstevel@tonic-gate 		if (cn & 01) {
3847c478bd9Sstevel@tonic-gate 			cn = (((unsigned int)*fp++ & 0xf0) >> 4);
3857c478bd9Sstevel@tonic-gate 			cn += (*fp << 4);
3867c478bd9Sstevel@tonic-gate 		} else {
3877c478bd9Sstevel@tonic-gate 			cn = *fp++;
3887c478bd9Sstevel@tonic-gate 			cn += ((*fp & 0x0f) << 8);
3897c478bd9Sstevel@tonic-gate 		}
3907c478bd9Sstevel@tonic-gate 		if (cn >= PCF_12BCLUSTER)
3917c478bd9Sstevel@tonic-gate 			cn |= PCF_RESCLUSTER;
392f127cb91Sfrankho 		break;
393f127cb91Sfrankho 	default:
394f127cb91Sfrankho 		pc_mark_irrecov(fsp);
395f127cb91Sfrankho 		cn = PCF_ERRORCLUSTER;
3967c478bd9Sstevel@tonic-gate 	}
3977c478bd9Sstevel@tonic-gate 	return (cn);
3987c478bd9Sstevel@tonic-gate }
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate /*
4017c478bd9Sstevel@tonic-gate  * Set a cluster in the FAT to a value.
4027c478bd9Sstevel@tonic-gate  *	cn = cluster number to be set in FAT
4037c478bd9Sstevel@tonic-gate  *	ncn = new value
4047c478bd9Sstevel@tonic-gate  */
4057c478bd9Sstevel@tonic-gate void
pc_setcluster(struct pcfs * fsp,pc_cluster32_t cn,pc_cluster32_t ncn)4067c478bd9Sstevel@tonic-gate pc_setcluster(struct pcfs *fsp, pc_cluster32_t cn, pc_cluster32_t ncn)
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	unsigned char *fp;
409f127cb91Sfrankho 	pc_cluster16_t ncn16;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
4127c478bd9Sstevel@tonic-gate 		panic("pc_setcluster");
4137c478bd9Sstevel@tonic-gate 	fsp->pcfs_flags |= PCFS_FATMOD;
4147c478bd9Sstevel@tonic-gate 	pc_mark_fat_updated(fsp, cn);
415f127cb91Sfrankho 	switch (fsp->pcfs_fattype) {
416f127cb91Sfrankho 	case FAT32:
4177c478bd9Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 2);
4187c478bd9Sstevel@tonic-gate 		*(pc_cluster32_t *)fp = htoli(ncn);
419f127cb91Sfrankho 		break;
420f127cb91Sfrankho 	case FAT16:
4217c478bd9Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 1);
4227c478bd9Sstevel@tonic-gate 		ncn16 = (pc_cluster16_t)ncn;
4237c478bd9Sstevel@tonic-gate 		*(pc_cluster16_t *)fp = htols(ncn16);
424f127cb91Sfrankho 		break;
425f127cb91Sfrankho 	case FAT12:
4267c478bd9Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
4277c478bd9Sstevel@tonic-gate 		if (cn & 01) {
4287c478bd9Sstevel@tonic-gate 			*fp = (*fp & 0x0f) | ((ncn << 4) & 0xf0);
4297c478bd9Sstevel@tonic-gate 			fp++;
4307c478bd9Sstevel@tonic-gate 			*fp = (ncn >> 4) & 0xff;
4317c478bd9Sstevel@tonic-gate 		} else {
4327c478bd9Sstevel@tonic-gate 			*fp++ = ncn & 0xff;
4337c478bd9Sstevel@tonic-gate 			*fp = (*fp & 0xf0) | ((ncn >> 8) & 0x0f);
4347c478bd9Sstevel@tonic-gate 		}
435f127cb91Sfrankho 		break;
436f127cb91Sfrankho 	default:
437f127cb91Sfrankho 		pc_mark_irrecov(fsp);
4387c478bd9Sstevel@tonic-gate 	}
4397c478bd9Sstevel@tonic-gate 	if (ncn == PCF_FREECLUSTER) {
4407c478bd9Sstevel@tonic-gate 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
4417c478bd9Sstevel@tonic-gate 		if (IS_FAT32(fsp)) {
442f127cb91Sfrankho 			if (fsp->pcfs_fsinfo.fs_free_clusters !=
4437c478bd9Sstevel@tonic-gate 			    FSINFO_UNKNOWN)
444f127cb91Sfrankho 				fsp->pcfs_fsinfo.fs_free_clusters++;
4457c478bd9Sstevel@tonic-gate 		}
4467c478bd9Sstevel@tonic-gate 	}
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate /*
4507c478bd9Sstevel@tonic-gate  * Allocate a new cluster.
4517c478bd9Sstevel@tonic-gate  */
4527c478bd9Sstevel@tonic-gate pc_cluster32_t
pc_alloccluster(struct pcfs * fsp,int zwrite)4537c478bd9Sstevel@tonic-gate pc_alloccluster(
4547c478bd9Sstevel@tonic-gate 	struct pcfs *fsp,	/* file sys to allocate in */
4557c478bd9Sstevel@tonic-gate 	int zwrite)			/* boolean for writing zeroes */
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate 	pc_cluster32_t cn;
4587c478bd9Sstevel@tonic-gate 	int	error;
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
4617c478bd9Sstevel@tonic-gate 		panic("pc_addcluster: no FAT");
4627c478bd9Sstevel@tonic-gate 
463*32d46495SMichael Bergknoff 	for (cn = fsp->pcfs_nxfrecls; pc_validcl(fsp, cn); cn++) {
4647c478bd9Sstevel@tonic-gate 		if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
4657c478bd9Sstevel@tonic-gate 			struct buf *bp;
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 			if (IS_FAT32(fsp)) {
4687c478bd9Sstevel@tonic-gate 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK32);
469f127cb91Sfrankho 				if (fsp->pcfs_fsinfo.fs_free_clusters !=
4707c478bd9Sstevel@tonic-gate 				    FSINFO_UNKNOWN)
471f127cb91Sfrankho 					fsp->pcfs_fsinfo.fs_free_clusters--;
4727c478bd9Sstevel@tonic-gate 			} else
4737c478bd9Sstevel@tonic-gate 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK);
4747c478bd9Sstevel@tonic-gate 			if (zwrite) {
4757c478bd9Sstevel@tonic-gate 				/*
4767c478bd9Sstevel@tonic-gate 				 * zero the new cluster
4777c478bd9Sstevel@tonic-gate 				 */
4787c478bd9Sstevel@tonic-gate 				bp = ngeteblk(fsp->pcfs_clsize);
4797c478bd9Sstevel@tonic-gate 				bp->b_edev = fsp->pcfs_xdev;
4807c478bd9Sstevel@tonic-gate 				bp->b_dev = cmpdev(bp->b_edev);
4817c478bd9Sstevel@tonic-gate 				bp->b_blkno = pc_cldaddr(fsp, cn);
4827c478bd9Sstevel@tonic-gate 				clrbuf(bp);
4837c478bd9Sstevel@tonic-gate 				bwrite2(bp);
4847c478bd9Sstevel@tonic-gate 				error = geterror(bp);
4857c478bd9Sstevel@tonic-gate 				brelse(bp);
4867c478bd9Sstevel@tonic-gate 				if (error) {
4877c478bd9Sstevel@tonic-gate 					pc_mark_irrecov(fsp);
4887c478bd9Sstevel@tonic-gate 					return (PCF_ERRORCLUSTER);
4897c478bd9Sstevel@tonic-gate 				}
4907c478bd9Sstevel@tonic-gate 			}
4917c478bd9Sstevel@tonic-gate 			fsp->pcfs_nxfrecls = cn + 1;
4927c478bd9Sstevel@tonic-gate 			return (cn);
4937c478bd9Sstevel@tonic-gate 		}
4947c478bd9Sstevel@tonic-gate 	}
4957c478bd9Sstevel@tonic-gate 	return (PCF_FREECLUSTER);
4967c478bd9Sstevel@tonic-gate }
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate /*
4997c478bd9Sstevel@tonic-gate  * Get the number of clusters used by a file or subdirectory
5007c478bd9Sstevel@tonic-gate  */
5017c478bd9Sstevel@tonic-gate int
pc_fileclsize(struct pcfs * fsp,pc_cluster32_t startcl,pc_cluster32_t * ncl)5027c478bd9Sstevel@tonic-gate pc_fileclsize(
5037c478bd9Sstevel@tonic-gate 	struct pcfs *fsp,
5049bd42341Sfrankho 	pc_cluster32_t startcl, pc_cluster32_t *ncl)
5057c478bd9Sstevel@tonic-gate {
5067c478bd9Sstevel@tonic-gate 	int count = 0;
5077c478bd9Sstevel@tonic-gate 
5089bd42341Sfrankho 	*ncl = 0;
5099bd42341Sfrankho 	for (count = 0; pc_validcl(fsp, startcl);
5109bd42341Sfrankho 	    startcl = pc_getcluster(fsp, startcl)) {
5119bd42341Sfrankho 		if (count++ >= fsp->pcfs_ncluster)
5129bd42341Sfrankho 			return (EIO);
5137c478bd9Sstevel@tonic-gate 	}
5149bd42341Sfrankho 	*ncl = (pc_cluster32_t)count;
5159bd42341Sfrankho 
5169bd42341Sfrankho 	return (0);
5177c478bd9Sstevel@tonic-gate }
518