xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_alloc.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Routines to allocate and deallocate data blocks on the disk
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <sys/param.h>
34*0Sstevel@tonic-gate #include <sys/errno.h>
35*0Sstevel@tonic-gate #include <sys/buf.h>
36*0Sstevel@tonic-gate #include <sys/vfs.h>
37*0Sstevel@tonic-gate #include <sys/vnode.h>
38*0Sstevel@tonic-gate #include <sys/cmn_err.h>
39*0Sstevel@tonic-gate #include <sys/debug.h>
40*0Sstevel@tonic-gate #include <sys/sysmacros.h>
41*0Sstevel@tonic-gate #include <sys/systm.h>
42*0Sstevel@tonic-gate #include <sys/fs/pc_label.h>
43*0Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
44*0Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
45*0Sstevel@tonic-gate #include <sys/fs/pc_node.h>
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate static pc_cluster32_t pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn);
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate /*
50*0Sstevel@tonic-gate  * Convert file logical block (cluster) numbers to disk block numbers.
51*0Sstevel@tonic-gate  * Also return number of physically contiguous blocks if asked for.
52*0Sstevel@tonic-gate  * Used for reading only. Use pc_balloc for writing.
53*0Sstevel@tonic-gate  */
54*0Sstevel@tonic-gate int
55*0Sstevel@tonic-gate pc_bmap(
56*0Sstevel@tonic-gate 	struct pcnode *pcp,		/* pcnode for file */
57*0Sstevel@tonic-gate 	daddr_t lcn,			/* logical cluster no */
58*0Sstevel@tonic-gate 	daddr_t *dbnp,			/* ptr to phys block no */
59*0Sstevel@tonic-gate 	uint_t *contigbp)		/* ptr to number of contiguous bytes */
60*0Sstevel@tonic-gate 					/* may be zero if not wanted */
61*0Sstevel@tonic-gate {
62*0Sstevel@tonic-gate 	struct pcfs *fsp;	/* pcfs that file is in */
63*0Sstevel@tonic-gate 	struct vnode *vp;
64*0Sstevel@tonic-gate 	pc_cluster32_t cn, ncn;		/* current, next cluster number */
65*0Sstevel@tonic-gate 	daddr_t olcn = lcn;
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate 	PC_DPRINTF2(6, "pc_bmap: pcp=0x%p, lcn=%ld\n", (void *)pcp, lcn);
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate 	vp = PCTOV(pcp);
70*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
71*0Sstevel@tonic-gate 	if (lcn < 0)
72*0Sstevel@tonic-gate 		return (ENOENT);
73*0Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
74*0Sstevel@tonic-gate 		daddr_t lbn, bn; /* logical (disk) block number */
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate 		lbn = pc_cltodb(fsp, lcn);
77*0Sstevel@tonic-gate 		if (lbn >= fsp->pcfs_rdirsec) {
78*0Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT1\n");
79*0Sstevel@tonic-gate 			return (ENOENT);
80*0Sstevel@tonic-gate 		}
81*0Sstevel@tonic-gate 		bn = fsp->pcfs_rdirstart + lbn;
82*0Sstevel@tonic-gate 		*dbnp = pc_dbdaddr(fsp, bn);
83*0Sstevel@tonic-gate 		if (contigbp) {
84*0Sstevel@tonic-gate 			ASSERT (*contigbp >= fsp->pcfs_secsize);
85*0Sstevel@tonic-gate 			*contigbp = MIN(*contigbp,
86*0Sstevel@tonic-gate 			    fsp->pcfs_secsize * (fsp->pcfs_rdirsec - lbn));
87*0Sstevel@tonic-gate 		}
88*0Sstevel@tonic-gate 	} else {
89*0Sstevel@tonic-gate 
90*0Sstevel@tonic-gate 		if (lcn >= fsp->pcfs_ncluster) {
91*0Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT2\n");
92*0Sstevel@tonic-gate 			return (ENOENT);
93*0Sstevel@tonic-gate 		}
94*0Sstevel@tonic-gate 		if (vp->v_type == VREG &&
95*0Sstevel@tonic-gate 		    (pcp->pc_size == 0 ||
96*0Sstevel@tonic-gate 		    lcn >= (daddr_t)howmany((offset_t)pcp->pc_size,
97*0Sstevel@tonic-gate 				fsp->pcfs_clsize))) {
98*0Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT3\n");
99*0Sstevel@tonic-gate 			return (ENOENT);
100*0Sstevel@tonic-gate 		}
101*0Sstevel@tonic-gate 		ncn = pcp->pc_scluster;
102*0Sstevel@tonic-gate 		if (IS_FAT32(fsp) && ncn == 0)
103*0Sstevel@tonic-gate 			ncn = fsp->pcfs_rdirstart;
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 		/* Do we have a cached index/cluster pair? */
106*0Sstevel@tonic-gate 		if (pcp->pc_lindex > 0 && lcn >= pcp->pc_lindex) {
107*0Sstevel@tonic-gate 			lcn -= pcp->pc_lindex;
108*0Sstevel@tonic-gate 			ncn = pcp->pc_lcluster;
109*0Sstevel@tonic-gate 		}
110*0Sstevel@tonic-gate 		do {
111*0Sstevel@tonic-gate 			cn = ncn;
112*0Sstevel@tonic-gate 			if (!pc_validcl(fsp, cn)) {
113*0Sstevel@tonic-gate 				if (IS_FAT32(fsp) && cn >= PCF_LASTCLUSTER32 &&
114*0Sstevel@tonic-gate 				    vp->v_type == VDIR) {
115*0Sstevel@tonic-gate 					PC_DPRINTF0(2, "pc_bmap: ENOENT4\n");
116*0Sstevel@tonic-gate 					return (ENOENT);
117*0Sstevel@tonic-gate 				} else if (!IS_FAT32(fsp) &&
118*0Sstevel@tonic-gate 				    cn >= PCF_LASTCLUSTER &&
119*0Sstevel@tonic-gate 				    vp->v_type == VDIR) {
120*0Sstevel@tonic-gate 					PC_DPRINTF0(2, "pc_bmap: ENOENT5\n");
121*0Sstevel@tonic-gate 					return (ENOENT);
122*0Sstevel@tonic-gate 				} else {
123*0Sstevel@tonic-gate 					PC_DPRINTF1(1,
124*0Sstevel@tonic-gate 					    "pc_bmap: badfs cn=%d\n", cn);
125*0Sstevel@tonic-gate 					(void) pc_badfs(fsp);
126*0Sstevel@tonic-gate 					return (EIO);
127*0Sstevel@tonic-gate 				}
128*0Sstevel@tonic-gate 			}
129*0Sstevel@tonic-gate 			ncn = pc_getcluster(fsp, cn);
130*0Sstevel@tonic-gate 		} while (lcn--);
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate 		/*
133*0Sstevel@tonic-gate 		 * Cache this cluster, as we'll most likely visit the
134*0Sstevel@tonic-gate 		 * one after this next time.  Considerably improves
135*0Sstevel@tonic-gate 		 * performance on sequential reads and writes.
136*0Sstevel@tonic-gate 		 */
137*0Sstevel@tonic-gate 		pcp->pc_lindex = olcn;
138*0Sstevel@tonic-gate 		pcp->pc_lcluster = cn;
139*0Sstevel@tonic-gate 		*dbnp = pc_cldaddr(fsp, cn);
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 		if (contigbp && *contigbp > fsp->pcfs_clsize) {
142*0Sstevel@tonic-gate 			uint_t count = fsp->pcfs_clsize;
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate 			while ((cn + 1) == ncn && count < *contigbp &&
145*0Sstevel@tonic-gate 			    pc_validcl(fsp, ncn)) {
146*0Sstevel@tonic-gate 				count += fsp->pcfs_clsize;
147*0Sstevel@tonic-gate 				cn = ncn;
148*0Sstevel@tonic-gate 				ncn = pc_getcluster(fsp, ncn);
149*0Sstevel@tonic-gate 			}
150*0Sstevel@tonic-gate 			*contigbp = count;
151*0Sstevel@tonic-gate 		}
152*0Sstevel@tonic-gate 	}
153*0Sstevel@tonic-gate 	return (0);
154*0Sstevel@tonic-gate }
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate /*
157*0Sstevel@tonic-gate  * Allocate file logical blocks (clusters).
158*0Sstevel@tonic-gate  * Return disk address of last allocated cluster.
159*0Sstevel@tonic-gate  */
160*0Sstevel@tonic-gate int
161*0Sstevel@tonic-gate pc_balloc(
162*0Sstevel@tonic-gate 	struct pcnode *pcp,	/* pcnode for file */
163*0Sstevel@tonic-gate 	daddr_t lcn,		/* logical cluster no */
164*0Sstevel@tonic-gate 	int zwrite,			/* zerofill blocks? */
165*0Sstevel@tonic-gate 	daddr_t *dbnp)			/* ptr to phys block no */
166*0Sstevel@tonic-gate {
167*0Sstevel@tonic-gate 	struct pcfs *fsp;	/* pcfs that file is in */
168*0Sstevel@tonic-gate 	struct vnode *vp;
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_balloc: pcp=0x%p, lcn=%ld\n", (void *)pcp, lcn);
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	vp = PCTOV(pcp);
173*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp -> v_vfsp);
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	if (lcn < 0) {
176*0Sstevel@tonic-gate 		return (EFBIG);
177*0Sstevel@tonic-gate 	}
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
180*0Sstevel@tonic-gate 		daddr_t lbn;
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 		lbn = pc_cltodb(fsp, lcn);
183*0Sstevel@tonic-gate 		if (lbn >= fsp->pcfs_rdirsec)
184*0Sstevel@tonic-gate 			return (ENOSPC);
185*0Sstevel@tonic-gate 		*dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
186*0Sstevel@tonic-gate 	} else {
187*0Sstevel@tonic-gate 		pc_cluster32_t cn;	/* current cluster number */
188*0Sstevel@tonic-gate 		pc_cluster32_t ncn;	/* next cluster number */
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 		if (lcn >= fsp->pcfs_ncluster)
191*0Sstevel@tonic-gate 			return (ENOSPC);
192*0Sstevel@tonic-gate 		if ((vp->v_type == VREG && pcp->pc_size == 0) ||
193*0Sstevel@tonic-gate 		    (vp->v_type == VDIR && lcn == 0)) {
194*0Sstevel@tonic-gate 			switch (cn = pc_alloccluster(fsp, 1)) {
195*0Sstevel@tonic-gate 			case PCF_FREECLUSTER:
196*0Sstevel@tonic-gate 				return (ENOSPC);
197*0Sstevel@tonic-gate 			case PCF_ERRORCLUSTER:
198*0Sstevel@tonic-gate 				return (EIO);
199*0Sstevel@tonic-gate 			}
200*0Sstevel@tonic-gate 			pcp->pc_scluster = cn;
201*0Sstevel@tonic-gate 		} else {
202*0Sstevel@tonic-gate 			cn = pcp->pc_scluster;
203*0Sstevel@tonic-gate 			if (IS_FAT32(fsp) && cn == 0)
204*0Sstevel@tonic-gate 				cn = fsp->pcfs_rdirstart;
205*0Sstevel@tonic-gate 			if (!pc_validcl(fsp, cn)) {
206*0Sstevel@tonic-gate 				PC_DPRINTF1(1, "pc_balloc: badfs cn=%d\n", cn);
207*0Sstevel@tonic-gate 				(void) pc_badfs(fsp);
208*0Sstevel@tonic-gate 				return (EIO);
209*0Sstevel@tonic-gate 			}
210*0Sstevel@tonic-gate 		}
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 		if (pcp->pc_lindex > 0 && lcn > pcp->pc_lindex) {
213*0Sstevel@tonic-gate 			lcn -= pcp->pc_lindex;
214*0Sstevel@tonic-gate 			cn = pcp->pc_lcluster;
215*0Sstevel@tonic-gate 		}
216*0Sstevel@tonic-gate 		while (lcn-- > 0) {
217*0Sstevel@tonic-gate 			ncn = pc_getcluster(fsp, cn);
218*0Sstevel@tonic-gate 			if ((IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32) ||
219*0Sstevel@tonic-gate 			    (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER)) {
220*0Sstevel@tonic-gate 				/*
221*0Sstevel@tonic-gate 				 * Extend file (no holes).
222*0Sstevel@tonic-gate 				 */
223*0Sstevel@tonic-gate 				switch (ncn = pc_alloccluster(fsp, zwrite)) {
224*0Sstevel@tonic-gate 				case PCF_FREECLUSTER:
225*0Sstevel@tonic-gate 					return (ENOSPC);
226*0Sstevel@tonic-gate 				case PCF_ERRORCLUSTER:
227*0Sstevel@tonic-gate 					return (EIO);
228*0Sstevel@tonic-gate 				}
229*0Sstevel@tonic-gate 				pc_setcluster(fsp, cn, ncn);
230*0Sstevel@tonic-gate 			} else if (!pc_validcl(fsp, ncn)) {
231*0Sstevel@tonic-gate 				PC_DPRINTF1(1,
232*0Sstevel@tonic-gate 				    "pc_balloc: badfs ncn=%d\n", ncn);
233*0Sstevel@tonic-gate 				(void) pc_badfs(fsp);
234*0Sstevel@tonic-gate 				return (EIO);
235*0Sstevel@tonic-gate 			}
236*0Sstevel@tonic-gate 			cn = ncn;
237*0Sstevel@tonic-gate 		}
238*0Sstevel@tonic-gate 		/*
239*0Sstevel@tonic-gate 		 * Do not cache the new cluster/index values; when
240*0Sstevel@tonic-gate 		 * extending the file we're interested in the last
241*0Sstevel@tonic-gate 		 * written cluster and not the last cluster allocated.
242*0Sstevel@tonic-gate 		 */
243*0Sstevel@tonic-gate 		*dbnp = pc_cldaddr(fsp, cn);
244*0Sstevel@tonic-gate 	}
245*0Sstevel@tonic-gate 	return (0);
246*0Sstevel@tonic-gate }
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate /*
249*0Sstevel@tonic-gate  * Free file cluster chain after the first skipcl clusters.
250*0Sstevel@tonic-gate  */
251*0Sstevel@tonic-gate int
252*0Sstevel@tonic-gate pc_bfree(struct pcnode *pcp, pc_cluster32_t skipcl)
253*0Sstevel@tonic-gate {
254*0Sstevel@tonic-gate 	struct pcfs *fsp;
255*0Sstevel@tonic-gate 	pc_cluster32_t cn;
256*0Sstevel@tonic-gate 	pc_cluster32_t ncn;
257*0Sstevel@tonic-gate 	int n;
258*0Sstevel@tonic-gate 	struct vnode *vp;
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 	vp = PCTOV(pcp);
261*0Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
262*0Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
263*0Sstevel@tonic-gate 		panic("pc_bfree");
264*0Sstevel@tonic-gate 	}
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_bfree: pcp=0x%p, after first %d clusters\n",
267*0Sstevel@tonic-gate 	    (void *)pcp, skipcl);
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	if (pcp->pc_size == 0 && vp->v_type == VREG) {
270*0Sstevel@tonic-gate 		return (0);
271*0Sstevel@tonic-gate 	}
272*0Sstevel@tonic-gate 	if (vp->v_type == VREG) {
273*0Sstevel@tonic-gate 		n = (int)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize);
274*0Sstevel@tonic-gate 		if (n > fsp->pcfs_ncluster) {
275*0Sstevel@tonic-gate 			PC_DPRINTF1(1, "pc_bfree: badfs n=%d\n", n);
276*0Sstevel@tonic-gate 			(void) pc_badfs(fsp);
277*0Sstevel@tonic-gate 			return (EIO);
278*0Sstevel@tonic-gate 		}
279*0Sstevel@tonic-gate 	} else {
280*0Sstevel@tonic-gate 		n = fsp->pcfs_ncluster;
281*0Sstevel@tonic-gate 	}
282*0Sstevel@tonic-gate 	cn = pcp->pc_scluster;
283*0Sstevel@tonic-gate 	if (IS_FAT32(fsp) && cn == 0)
284*0Sstevel@tonic-gate 		cn = fsp->pcfs_rdirstart;
285*0Sstevel@tonic-gate 	if (skipcl == 0) {
286*0Sstevel@tonic-gate 		if (IS_FAT32(fsp))
287*0Sstevel@tonic-gate 			pcp->pc_scluster = PCF_LASTCLUSTERMARK32;
288*0Sstevel@tonic-gate 		else
289*0Sstevel@tonic-gate 			pcp->pc_scluster = PCF_LASTCLUSTERMARK;
290*0Sstevel@tonic-gate 	}
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	/* Invalidate last used cluster cache */
293*0Sstevel@tonic-gate 	pcp->pc_lindex = 0;
294*0Sstevel@tonic-gate 	pcp->pc_lcluster = pcp->pc_scluster;
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	while (n--) {
297*0Sstevel@tonic-gate 		if (!pc_validcl(fsp, cn)) {
298*0Sstevel@tonic-gate 			PC_DPRINTF1(1, "pc_bfree: badfs cn=%d\n", cn);
299*0Sstevel@tonic-gate 			(void) pc_badfs(fsp);
300*0Sstevel@tonic-gate 			return (EIO);
301*0Sstevel@tonic-gate 		}
302*0Sstevel@tonic-gate 		ncn = pc_getcluster(fsp, cn);
303*0Sstevel@tonic-gate 		if (skipcl == 0) {
304*0Sstevel@tonic-gate 			pc_setcluster(fsp, cn, PCF_FREECLUSTER);
305*0Sstevel@tonic-gate 		} else {
306*0Sstevel@tonic-gate 			skipcl--;
307*0Sstevel@tonic-gate 			if (skipcl == 0) {
308*0Sstevel@tonic-gate 				if (IS_FAT32(fsp)) {
309*0Sstevel@tonic-gate 					pc_setcluster(fsp, cn,
310*0Sstevel@tonic-gate 					    PCF_LASTCLUSTERMARK32);
311*0Sstevel@tonic-gate 				} else
312*0Sstevel@tonic-gate 					pc_setcluster(fsp, cn,
313*0Sstevel@tonic-gate 					    PCF_LASTCLUSTERMARK);
314*0Sstevel@tonic-gate 			}
315*0Sstevel@tonic-gate 		}
316*0Sstevel@tonic-gate 		if (IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32 &&
317*0Sstevel@tonic-gate 		    vp->v_type == VDIR)
318*0Sstevel@tonic-gate 			break;
319*0Sstevel@tonic-gate 		if (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER &&
320*0Sstevel@tonic-gate 		    vp->v_type == VDIR)
321*0Sstevel@tonic-gate 			break;
322*0Sstevel@tonic-gate 		cn = ncn;
323*0Sstevel@tonic-gate 	}
324*0Sstevel@tonic-gate 	return (0);
325*0Sstevel@tonic-gate }
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate /*
328*0Sstevel@tonic-gate  * Return the number of free blocks in the filesystem.
329*0Sstevel@tonic-gate  */
330*0Sstevel@tonic-gate int
331*0Sstevel@tonic-gate pc_freeclusters(struct pcfs *fsp)
332*0Sstevel@tonic-gate {
333*0Sstevel@tonic-gate 	pc_cluster32_t cn;
334*0Sstevel@tonic-gate 	int free;
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	/*
337*0Sstevel@tonic-gate 	 * make sure the FAT is in core
338*0Sstevel@tonic-gate 	 */
339*0Sstevel@tonic-gate 	free = 0;
340*0Sstevel@tonic-gate 	for (cn = PCF_FIRSTCLUSTER;
341*0Sstevel@tonic-gate 	    (int)cn <= fsp->pcfs_ncluster; cn++) {
342*0Sstevel@tonic-gate 		if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
343*0Sstevel@tonic-gate 			free++;
344*0Sstevel@tonic-gate 		}
345*0Sstevel@tonic-gate 	}
346*0Sstevel@tonic-gate 	return (free);
347*0Sstevel@tonic-gate }
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate /*
350*0Sstevel@tonic-gate  * Cluster manipulation routines.
351*0Sstevel@tonic-gate  * FAT must be resident.
352*0Sstevel@tonic-gate  */
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate /*
355*0Sstevel@tonic-gate  * Get the next cluster in the file cluster chain.
356*0Sstevel@tonic-gate  *	cn = current cluster number in chain
357*0Sstevel@tonic-gate  */
358*0Sstevel@tonic-gate static pc_cluster32_t
359*0Sstevel@tonic-gate pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn)
360*0Sstevel@tonic-gate {
361*0Sstevel@tonic-gate 	unsigned char *fp;
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 	PC_DPRINTF1(7, "pc_getcluster: cn=%x ", cn);
364*0Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
365*0Sstevel@tonic-gate 		panic("pc_getcluster");
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {	/* 32 bit FAT */
368*0Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 2);
369*0Sstevel@tonic-gate 		cn = ltohi(*(pc_cluster32_t *)fp);
370*0Sstevel@tonic-gate 	} else if (fsp->pcfs_flags & PCFS_FAT16) {	/* 16 bit FAT */
371*0Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 1);
372*0Sstevel@tonic-gate 		cn = ltohs(*(pc_cluster16_t *)fp);
373*0Sstevel@tonic-gate 	} else {	/* 12 bit FAT */
374*0Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
375*0Sstevel@tonic-gate 		if (cn & 01) {
376*0Sstevel@tonic-gate 			cn = (((unsigned int)*fp++ & 0xf0) >> 4);
377*0Sstevel@tonic-gate 			cn += (*fp << 4);
378*0Sstevel@tonic-gate 		} else {
379*0Sstevel@tonic-gate 			cn = *fp++;
380*0Sstevel@tonic-gate 			cn += ((*fp & 0x0f) << 8);
381*0Sstevel@tonic-gate 		}
382*0Sstevel@tonic-gate 		if (cn >= PCF_12BCLUSTER)
383*0Sstevel@tonic-gate 			cn |= PCF_RESCLUSTER;
384*0Sstevel@tonic-gate 	}
385*0Sstevel@tonic-gate 	PC_DPRINTF1(7, " %x\n", cn);
386*0Sstevel@tonic-gate 	return (cn);
387*0Sstevel@tonic-gate }
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate /*
390*0Sstevel@tonic-gate  * Set a cluster in the FAT to a value.
391*0Sstevel@tonic-gate  *	cn = cluster number to be set in FAT
392*0Sstevel@tonic-gate  *	ncn = new value
393*0Sstevel@tonic-gate  */
394*0Sstevel@tonic-gate void
395*0Sstevel@tonic-gate pc_setcluster(struct pcfs *fsp, pc_cluster32_t cn, pc_cluster32_t ncn)
396*0Sstevel@tonic-gate {
397*0Sstevel@tonic-gate 	unsigned char *fp;
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	PC_DPRINTF2(7, "pc_setcluster: cn=%d ncn=%d\n", cn, ncn);
400*0Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
401*0Sstevel@tonic-gate 		panic("pc_setcluster");
402*0Sstevel@tonic-gate 	fsp->pcfs_flags |= PCFS_FATMOD;
403*0Sstevel@tonic-gate 	pc_mark_fat_updated(fsp, cn);
404*0Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {	/* 32 bit FAT */
405*0Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 2);
406*0Sstevel@tonic-gate 		*(pc_cluster32_t *)fp = htoli(ncn);
407*0Sstevel@tonic-gate 	} else if (fsp->pcfs_flags & PCFS_FAT16) {	/* 16 bit FAT */
408*0Sstevel@tonic-gate 		pc_cluster16_t	ncn16;
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 1);
411*0Sstevel@tonic-gate 		ncn16 = (pc_cluster16_t)ncn;
412*0Sstevel@tonic-gate 		*(pc_cluster16_t *)fp = htols(ncn16);
413*0Sstevel@tonic-gate 	} else {	/* 12 bit FAT */
414*0Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
415*0Sstevel@tonic-gate 		if (cn & 01) {
416*0Sstevel@tonic-gate 			*fp = (*fp & 0x0f) | ((ncn << 4) & 0xf0);
417*0Sstevel@tonic-gate 			fp++;
418*0Sstevel@tonic-gate 			*fp = (ncn >> 4) & 0xff;
419*0Sstevel@tonic-gate 		} else {
420*0Sstevel@tonic-gate 			*fp++ = ncn & 0xff;
421*0Sstevel@tonic-gate 			*fp = (*fp & 0xf0) | ((ncn >> 8) & 0x0f);
422*0Sstevel@tonic-gate 		}
423*0Sstevel@tonic-gate 	}
424*0Sstevel@tonic-gate 	if (ncn == PCF_FREECLUSTER) {
425*0Sstevel@tonic-gate 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
426*0Sstevel@tonic-gate 		if (IS_FAT32(fsp)) {
427*0Sstevel@tonic-gate 			if (fsp->fsinfo_native.fs_free_clusters !=
428*0Sstevel@tonic-gate 			    FSINFO_UNKNOWN)
429*0Sstevel@tonic-gate 				fsp->fsinfo_native.fs_free_clusters++;
430*0Sstevel@tonic-gate 		}
431*0Sstevel@tonic-gate 	}
432*0Sstevel@tonic-gate }
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate /*
435*0Sstevel@tonic-gate  * Allocate a new cluster.
436*0Sstevel@tonic-gate  */
437*0Sstevel@tonic-gate pc_cluster32_t
438*0Sstevel@tonic-gate pc_alloccluster(
439*0Sstevel@tonic-gate 	struct pcfs *fsp,	/* file sys to allocate in */
440*0Sstevel@tonic-gate 	int zwrite)			/* boolean for writing zeroes */
441*0Sstevel@tonic-gate {
442*0Sstevel@tonic-gate 	pc_cluster32_t cn;
443*0Sstevel@tonic-gate 	int	error;
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
446*0Sstevel@tonic-gate 		panic("pc_addcluster: no FAT");
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 	for (cn = fsp->pcfs_nxfrecls;
449*0Sstevel@tonic-gate 	    (int)cn <= fsp->pcfs_ncluster; cn++) {
450*0Sstevel@tonic-gate 		if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
451*0Sstevel@tonic-gate 			struct buf *bp;
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 			if (IS_FAT32(fsp)) {
454*0Sstevel@tonic-gate 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK32);
455*0Sstevel@tonic-gate 				if (fsp->fsinfo_native.fs_free_clusters !=
456*0Sstevel@tonic-gate 				    FSINFO_UNKNOWN)
457*0Sstevel@tonic-gate 					fsp->fsinfo_native.fs_free_clusters--;
458*0Sstevel@tonic-gate 			} else
459*0Sstevel@tonic-gate 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK);
460*0Sstevel@tonic-gate 			if (zwrite) {
461*0Sstevel@tonic-gate 				/*
462*0Sstevel@tonic-gate 				 * zero the new cluster
463*0Sstevel@tonic-gate 				 */
464*0Sstevel@tonic-gate 				bp = ngeteblk(fsp->pcfs_clsize);
465*0Sstevel@tonic-gate 				bp->b_edev = fsp->pcfs_xdev;
466*0Sstevel@tonic-gate 				bp->b_dev = cmpdev(bp->b_edev);
467*0Sstevel@tonic-gate 				bp->b_blkno = pc_cldaddr(fsp, cn);
468*0Sstevel@tonic-gate 				clrbuf(bp);
469*0Sstevel@tonic-gate 				bwrite2(bp);
470*0Sstevel@tonic-gate 				error = geterror(bp);
471*0Sstevel@tonic-gate 				brelse(bp);
472*0Sstevel@tonic-gate 				if (error) {
473*0Sstevel@tonic-gate 					PC_DPRINTF0(1,
474*0Sstevel@tonic-gate 					    "pc_alloccluster: error\n");
475*0Sstevel@tonic-gate 					pc_mark_irrecov(fsp);
476*0Sstevel@tonic-gate 					return (PCF_ERRORCLUSTER);
477*0Sstevel@tonic-gate 				}
478*0Sstevel@tonic-gate 			}
479*0Sstevel@tonic-gate 			fsp->pcfs_nxfrecls = cn + 1;
480*0Sstevel@tonic-gate 			PC_DPRINTF1(5, "pc_alloccluster: new cluster = %d\n",
481*0Sstevel@tonic-gate 			    cn);
482*0Sstevel@tonic-gate 			return (cn);
483*0Sstevel@tonic-gate 		}
484*0Sstevel@tonic-gate 	}
485*0Sstevel@tonic-gate 	return (PCF_FREECLUSTER);
486*0Sstevel@tonic-gate }
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate /*
489*0Sstevel@tonic-gate  * Get the number of clusters used by a file or subdirectory
490*0Sstevel@tonic-gate  */
491*0Sstevel@tonic-gate int
492*0Sstevel@tonic-gate pc_fileclsize(
493*0Sstevel@tonic-gate 	struct pcfs *fsp,
494*0Sstevel@tonic-gate 	pc_cluster32_t strtcluster)
495*0Sstevel@tonic-gate {
496*0Sstevel@tonic-gate 	int count = 0;
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 	while (pc_validcl(fsp, strtcluster)) {
499*0Sstevel@tonic-gate 		count++;
500*0Sstevel@tonic-gate 		strtcluster = pc_getcluster(fsp, strtcluster);
501*0Sstevel@tonic-gate 	}
502*0Sstevel@tonic-gate 	return (count);
503*0Sstevel@tonic-gate }
504