xref: /onnv-gate/usr/src/uts/common/fs/pcfs/pc_alloc.c (revision 2972:10096047b1c0)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52720Sfrankho  * Common Development and Distribution License (the "License").
62720Sfrankho  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
222720Sfrankho  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Routines to allocate and deallocate data blocks on the disk
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <sys/param.h>
330Sstevel@tonic-gate #include <sys/errno.h>
340Sstevel@tonic-gate #include <sys/buf.h>
350Sstevel@tonic-gate #include <sys/vfs.h>
360Sstevel@tonic-gate #include <sys/vnode.h>
370Sstevel@tonic-gate #include <sys/cmn_err.h>
380Sstevel@tonic-gate #include <sys/debug.h>
390Sstevel@tonic-gate #include <sys/sysmacros.h>
400Sstevel@tonic-gate #include <sys/systm.h>
410Sstevel@tonic-gate #include <sys/fs/pc_label.h>
420Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
430Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
440Sstevel@tonic-gate #include <sys/fs/pc_node.h>
450Sstevel@tonic-gate 
460Sstevel@tonic-gate static pc_cluster32_t pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn);
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  * Convert file logical block (cluster) numbers to disk block numbers.
500Sstevel@tonic-gate  * Also return number of physically contiguous blocks if asked for.
510Sstevel@tonic-gate  * Used for reading only. Use pc_balloc for writing.
520Sstevel@tonic-gate  */
530Sstevel@tonic-gate int
540Sstevel@tonic-gate pc_bmap(
550Sstevel@tonic-gate 	struct pcnode *pcp,		/* pcnode for file */
560Sstevel@tonic-gate 	daddr_t lcn,			/* logical cluster no */
570Sstevel@tonic-gate 	daddr_t *dbnp,			/* ptr to phys block no */
580Sstevel@tonic-gate 	uint_t *contigbp)		/* ptr to number of contiguous bytes */
590Sstevel@tonic-gate 					/* may be zero if not wanted */
600Sstevel@tonic-gate {
610Sstevel@tonic-gate 	struct pcfs *fsp;	/* pcfs that file is in */
620Sstevel@tonic-gate 	struct vnode *vp;
630Sstevel@tonic-gate 	pc_cluster32_t cn, ncn;		/* current, next cluster number */
640Sstevel@tonic-gate 	daddr_t olcn = lcn;
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 	PC_DPRINTF2(6, "pc_bmap: pcp=0x%p, lcn=%ld\n", (void *)pcp, lcn);
670Sstevel@tonic-gate 
680Sstevel@tonic-gate 	vp = PCTOV(pcp);
690Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
700Sstevel@tonic-gate 	if (lcn < 0)
710Sstevel@tonic-gate 		return (ENOENT);
720Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
730Sstevel@tonic-gate 		daddr_t lbn, bn; /* logical (disk) block number */
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 		lbn = pc_cltodb(fsp, lcn);
760Sstevel@tonic-gate 		if (lbn >= fsp->pcfs_rdirsec) {
770Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT1\n");
780Sstevel@tonic-gate 			return (ENOENT);
790Sstevel@tonic-gate 		}
800Sstevel@tonic-gate 		bn = fsp->pcfs_rdirstart + lbn;
810Sstevel@tonic-gate 		*dbnp = pc_dbdaddr(fsp, bn);
820Sstevel@tonic-gate 		if (contigbp) {
830Sstevel@tonic-gate 			ASSERT (*contigbp >= fsp->pcfs_secsize);
840Sstevel@tonic-gate 			*contigbp = MIN(*contigbp,
850Sstevel@tonic-gate 			    fsp->pcfs_secsize * (fsp->pcfs_rdirsec - lbn));
860Sstevel@tonic-gate 		}
870Sstevel@tonic-gate 	} else {
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 		if (lcn >= fsp->pcfs_ncluster) {
900Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT2\n");
910Sstevel@tonic-gate 			return (ENOENT);
920Sstevel@tonic-gate 		}
930Sstevel@tonic-gate 		if (vp->v_type == VREG &&
940Sstevel@tonic-gate 		    (pcp->pc_size == 0 ||
950Sstevel@tonic-gate 		    lcn >= (daddr_t)howmany((offset_t)pcp->pc_size,
960Sstevel@tonic-gate 				fsp->pcfs_clsize))) {
970Sstevel@tonic-gate 			PC_DPRINTF0(2, "pc_bmap: ENOENT3\n");
980Sstevel@tonic-gate 			return (ENOENT);
990Sstevel@tonic-gate 		}
1000Sstevel@tonic-gate 		ncn = pcp->pc_scluster;
1010Sstevel@tonic-gate 		if (IS_FAT32(fsp) && ncn == 0)
1020Sstevel@tonic-gate 			ncn = fsp->pcfs_rdirstart;
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 		/* Do we have a cached index/cluster pair? */
1050Sstevel@tonic-gate 		if (pcp->pc_lindex > 0 && lcn >= pcp->pc_lindex) {
1060Sstevel@tonic-gate 			lcn -= pcp->pc_lindex;
1070Sstevel@tonic-gate 			ncn = pcp->pc_lcluster;
1080Sstevel@tonic-gate 		}
1090Sstevel@tonic-gate 		do {
1100Sstevel@tonic-gate 			cn = ncn;
1110Sstevel@tonic-gate 			if (!pc_validcl(fsp, cn)) {
1120Sstevel@tonic-gate 				if (IS_FAT32(fsp) && cn >= PCF_LASTCLUSTER32 &&
1130Sstevel@tonic-gate 				    vp->v_type == VDIR) {
1140Sstevel@tonic-gate 					PC_DPRINTF0(2, "pc_bmap: ENOENT4\n");
1150Sstevel@tonic-gate 					return (ENOENT);
1160Sstevel@tonic-gate 				} else if (!IS_FAT32(fsp) &&
1170Sstevel@tonic-gate 				    cn >= PCF_LASTCLUSTER &&
1180Sstevel@tonic-gate 				    vp->v_type == VDIR) {
1190Sstevel@tonic-gate 					PC_DPRINTF0(2, "pc_bmap: ENOENT5\n");
1200Sstevel@tonic-gate 					return (ENOENT);
1210Sstevel@tonic-gate 				} else {
1220Sstevel@tonic-gate 					PC_DPRINTF1(1,
1230Sstevel@tonic-gate 					    "pc_bmap: badfs cn=%d\n", cn);
1240Sstevel@tonic-gate 					(void) pc_badfs(fsp);
1250Sstevel@tonic-gate 					return (EIO);
1260Sstevel@tonic-gate 				}
1270Sstevel@tonic-gate 			}
1280Sstevel@tonic-gate 			ncn = pc_getcluster(fsp, cn);
1290Sstevel@tonic-gate 		} while (lcn--);
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 		/*
1320Sstevel@tonic-gate 		 * Cache this cluster, as we'll most likely visit the
1330Sstevel@tonic-gate 		 * one after this next time.  Considerably improves
1340Sstevel@tonic-gate 		 * performance on sequential reads and writes.
1350Sstevel@tonic-gate 		 */
1360Sstevel@tonic-gate 		pcp->pc_lindex = olcn;
1370Sstevel@tonic-gate 		pcp->pc_lcluster = cn;
1380Sstevel@tonic-gate 		*dbnp = pc_cldaddr(fsp, cn);
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 		if (contigbp && *contigbp > fsp->pcfs_clsize) {
1410Sstevel@tonic-gate 			uint_t count = fsp->pcfs_clsize;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 			while ((cn + 1) == ncn && count < *contigbp &&
1440Sstevel@tonic-gate 			    pc_validcl(fsp, ncn)) {
1450Sstevel@tonic-gate 				count += fsp->pcfs_clsize;
1460Sstevel@tonic-gate 				cn = ncn;
1470Sstevel@tonic-gate 				ncn = pc_getcluster(fsp, ncn);
1480Sstevel@tonic-gate 			}
1490Sstevel@tonic-gate 			*contigbp = count;
1500Sstevel@tonic-gate 		}
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 	return (0);
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate /*
1560Sstevel@tonic-gate  * Allocate file logical blocks (clusters).
1570Sstevel@tonic-gate  * Return disk address of last allocated cluster.
1580Sstevel@tonic-gate  */
1590Sstevel@tonic-gate int
1600Sstevel@tonic-gate pc_balloc(
1610Sstevel@tonic-gate 	struct pcnode *pcp,	/* pcnode for file */
1620Sstevel@tonic-gate 	daddr_t lcn,		/* logical cluster no */
1630Sstevel@tonic-gate 	int zwrite,			/* zerofill blocks? */
1640Sstevel@tonic-gate 	daddr_t *dbnp)			/* ptr to phys block no */
1650Sstevel@tonic-gate {
1660Sstevel@tonic-gate 	struct pcfs *fsp;	/* pcfs that file is in */
1670Sstevel@tonic-gate 	struct vnode *vp;
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_balloc: pcp=0x%p, lcn=%ld\n", (void *)pcp, lcn);
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	vp = PCTOV(pcp);
1720Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp -> v_vfsp);
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	if (lcn < 0) {
1750Sstevel@tonic-gate 		return (EFBIG);
1760Sstevel@tonic-gate 	}
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
1790Sstevel@tonic-gate 		daddr_t lbn;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 		lbn = pc_cltodb(fsp, lcn);
1820Sstevel@tonic-gate 		if (lbn >= fsp->pcfs_rdirsec)
1830Sstevel@tonic-gate 			return (ENOSPC);
1840Sstevel@tonic-gate 		*dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
1850Sstevel@tonic-gate 	} else {
1860Sstevel@tonic-gate 		pc_cluster32_t cn;	/* current cluster number */
1870Sstevel@tonic-gate 		pc_cluster32_t ncn;	/* next cluster number */
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 		if (lcn >= fsp->pcfs_ncluster)
1900Sstevel@tonic-gate 			return (ENOSPC);
1910Sstevel@tonic-gate 		if ((vp->v_type == VREG && pcp->pc_size == 0) ||
1920Sstevel@tonic-gate 		    (vp->v_type == VDIR && lcn == 0)) {
1930Sstevel@tonic-gate 			switch (cn = pc_alloccluster(fsp, 1)) {
1940Sstevel@tonic-gate 			case PCF_FREECLUSTER:
1950Sstevel@tonic-gate 				return (ENOSPC);
1960Sstevel@tonic-gate 			case PCF_ERRORCLUSTER:
1970Sstevel@tonic-gate 				return (EIO);
1980Sstevel@tonic-gate 			}
1990Sstevel@tonic-gate 			pcp->pc_scluster = cn;
2000Sstevel@tonic-gate 		} else {
2010Sstevel@tonic-gate 			cn = pcp->pc_scluster;
2020Sstevel@tonic-gate 			if (IS_FAT32(fsp) && cn == 0)
2030Sstevel@tonic-gate 				cn = fsp->pcfs_rdirstart;
2040Sstevel@tonic-gate 			if (!pc_validcl(fsp, cn)) {
2050Sstevel@tonic-gate 				PC_DPRINTF1(1, "pc_balloc: badfs cn=%d\n", cn);
2060Sstevel@tonic-gate 				(void) pc_badfs(fsp);
2070Sstevel@tonic-gate 				return (EIO);
2080Sstevel@tonic-gate 			}
2090Sstevel@tonic-gate 		}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		if (pcp->pc_lindex > 0 && lcn > pcp->pc_lindex) {
2120Sstevel@tonic-gate 			lcn -= pcp->pc_lindex;
2130Sstevel@tonic-gate 			cn = pcp->pc_lcluster;
2140Sstevel@tonic-gate 		}
2150Sstevel@tonic-gate 		while (lcn-- > 0) {
2160Sstevel@tonic-gate 			ncn = pc_getcluster(fsp, cn);
2170Sstevel@tonic-gate 			if ((IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32) ||
2180Sstevel@tonic-gate 			    (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER)) {
2190Sstevel@tonic-gate 				/*
2200Sstevel@tonic-gate 				 * Extend file (no holes).
2210Sstevel@tonic-gate 				 */
2220Sstevel@tonic-gate 				switch (ncn = pc_alloccluster(fsp, zwrite)) {
2230Sstevel@tonic-gate 				case PCF_FREECLUSTER:
2240Sstevel@tonic-gate 					return (ENOSPC);
2250Sstevel@tonic-gate 				case PCF_ERRORCLUSTER:
2260Sstevel@tonic-gate 					return (EIO);
2270Sstevel@tonic-gate 				}
2280Sstevel@tonic-gate 				pc_setcluster(fsp, cn, ncn);
2290Sstevel@tonic-gate 			} else if (!pc_validcl(fsp, ncn)) {
2300Sstevel@tonic-gate 				PC_DPRINTF1(1,
2310Sstevel@tonic-gate 				    "pc_balloc: badfs ncn=%d\n", ncn);
2320Sstevel@tonic-gate 				(void) pc_badfs(fsp);
2330Sstevel@tonic-gate 				return (EIO);
2340Sstevel@tonic-gate 			}
2350Sstevel@tonic-gate 			cn = ncn;
2360Sstevel@tonic-gate 		}
2370Sstevel@tonic-gate 		/*
2380Sstevel@tonic-gate 		 * Do not cache the new cluster/index values; when
2390Sstevel@tonic-gate 		 * extending the file we're interested in the last
2400Sstevel@tonic-gate 		 * written cluster and not the last cluster allocated.
2410Sstevel@tonic-gate 		 */
2420Sstevel@tonic-gate 		*dbnp = pc_cldaddr(fsp, cn);
2430Sstevel@tonic-gate 	}
2440Sstevel@tonic-gate 	return (0);
2450Sstevel@tonic-gate }
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate /*
2480Sstevel@tonic-gate  * Free file cluster chain after the first skipcl clusters.
2490Sstevel@tonic-gate  */
2500Sstevel@tonic-gate int
2510Sstevel@tonic-gate pc_bfree(struct pcnode *pcp, pc_cluster32_t skipcl)
2520Sstevel@tonic-gate {
2530Sstevel@tonic-gate 	struct pcfs *fsp;
2540Sstevel@tonic-gate 	pc_cluster32_t cn;
2550Sstevel@tonic-gate 	pc_cluster32_t ncn;
2560Sstevel@tonic-gate 	int n;
2570Sstevel@tonic-gate 	struct vnode *vp;
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	vp = PCTOV(pcp);
2600Sstevel@tonic-gate 	fsp = VFSTOPCFS(vp->v_vfsp);
2610Sstevel@tonic-gate 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
2620Sstevel@tonic-gate 		panic("pc_bfree");
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	PC_DPRINTF2(5, "pc_bfree: pcp=0x%p, after first %d clusters\n",
2660Sstevel@tonic-gate 	    (void *)pcp, skipcl);
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	if (pcp->pc_size == 0 && vp->v_type == VREG) {
2690Sstevel@tonic-gate 		return (0);
2700Sstevel@tonic-gate 	}
2710Sstevel@tonic-gate 	if (vp->v_type == VREG) {
2720Sstevel@tonic-gate 		n = (int)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize);
2730Sstevel@tonic-gate 		if (n > fsp->pcfs_ncluster) {
2740Sstevel@tonic-gate 			PC_DPRINTF1(1, "pc_bfree: badfs n=%d\n", n);
2750Sstevel@tonic-gate 			(void) pc_badfs(fsp);
2760Sstevel@tonic-gate 			return (EIO);
2770Sstevel@tonic-gate 		}
2780Sstevel@tonic-gate 	} else {
2790Sstevel@tonic-gate 		n = fsp->pcfs_ncluster;
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 	cn = pcp->pc_scluster;
2820Sstevel@tonic-gate 	if (IS_FAT32(fsp) && cn == 0)
2830Sstevel@tonic-gate 		cn = fsp->pcfs_rdirstart;
2840Sstevel@tonic-gate 	if (skipcl == 0) {
2850Sstevel@tonic-gate 		if (IS_FAT32(fsp))
2860Sstevel@tonic-gate 			pcp->pc_scluster = PCF_LASTCLUSTERMARK32;
2870Sstevel@tonic-gate 		else
2880Sstevel@tonic-gate 			pcp->pc_scluster = PCF_LASTCLUSTERMARK;
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/* Invalidate last used cluster cache */
2920Sstevel@tonic-gate 	pcp->pc_lindex = 0;
2930Sstevel@tonic-gate 	pcp->pc_lcluster = pcp->pc_scluster;
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	while (n--) {
2960Sstevel@tonic-gate 		if (!pc_validcl(fsp, cn)) {
2970Sstevel@tonic-gate 			PC_DPRINTF1(1, "pc_bfree: badfs cn=%d\n", cn);
2980Sstevel@tonic-gate 			(void) pc_badfs(fsp);
2990Sstevel@tonic-gate 			return (EIO);
3000Sstevel@tonic-gate 		}
3010Sstevel@tonic-gate 		ncn = pc_getcluster(fsp, cn);
3020Sstevel@tonic-gate 		if (skipcl == 0) {
3030Sstevel@tonic-gate 			pc_setcluster(fsp, cn, PCF_FREECLUSTER);
3040Sstevel@tonic-gate 		} else {
3050Sstevel@tonic-gate 			skipcl--;
3060Sstevel@tonic-gate 			if (skipcl == 0) {
3070Sstevel@tonic-gate 				if (IS_FAT32(fsp)) {
3080Sstevel@tonic-gate 					pc_setcluster(fsp, cn,
3090Sstevel@tonic-gate 					    PCF_LASTCLUSTERMARK32);
3100Sstevel@tonic-gate 				} else
3110Sstevel@tonic-gate 					pc_setcluster(fsp, cn,
3120Sstevel@tonic-gate 					    PCF_LASTCLUSTERMARK);
3130Sstevel@tonic-gate 			}
3140Sstevel@tonic-gate 		}
3150Sstevel@tonic-gate 		if (IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32 &&
3160Sstevel@tonic-gate 		    vp->v_type == VDIR)
3170Sstevel@tonic-gate 			break;
3180Sstevel@tonic-gate 		if (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER &&
3190Sstevel@tonic-gate 		    vp->v_type == VDIR)
3200Sstevel@tonic-gate 			break;
3210Sstevel@tonic-gate 		cn = ncn;
3220Sstevel@tonic-gate 	}
3230Sstevel@tonic-gate 	return (0);
3240Sstevel@tonic-gate }
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate /*
3270Sstevel@tonic-gate  * Return the number of free blocks in the filesystem.
3280Sstevel@tonic-gate  */
3290Sstevel@tonic-gate int
3300Sstevel@tonic-gate pc_freeclusters(struct pcfs *fsp)
3310Sstevel@tonic-gate {
3320Sstevel@tonic-gate 	pc_cluster32_t cn;
3332720Sfrankho 	int free = 0;
3342720Sfrankho 
3352720Sfrankho 	if (IS_FAT32(fsp) &&
3362720Sfrankho 	    fsp->fsinfo_native.fs_free_clusters != FSINFO_UNKNOWN)
3372720Sfrankho 		return (fsp->fsinfo_native.fs_free_clusters);
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	/*
3400Sstevel@tonic-gate 	 * make sure the FAT is in core
3410Sstevel@tonic-gate 	 */
3420Sstevel@tonic-gate 	for (cn = PCF_FIRSTCLUSTER;
3432720Sfrankho 	    (int)cn < fsp->pcfs_ncluster; cn++) {
3440Sstevel@tonic-gate 		if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
3450Sstevel@tonic-gate 			free++;
3460Sstevel@tonic-gate 		}
3470Sstevel@tonic-gate 	}
3482720Sfrankho 
3492720Sfrankho 	if (IS_FAT32(fsp)) {
3502720Sfrankho 		ASSERT(fsp->fsinfo_native.fs_free_clusters == FSINFO_UNKNOWN);
3512720Sfrankho 		fsp->fsinfo_native.fs_free_clusters = free;
3522720Sfrankho 	}
3530Sstevel@tonic-gate 	return (free);
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate /*
3570Sstevel@tonic-gate  * Cluster manipulation routines.
3580Sstevel@tonic-gate  * FAT must be resident.
3590Sstevel@tonic-gate  */
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate /*
3620Sstevel@tonic-gate  * Get the next cluster in the file cluster chain.
3630Sstevel@tonic-gate  *	cn = current cluster number in chain
3640Sstevel@tonic-gate  */
3650Sstevel@tonic-gate static pc_cluster32_t
3660Sstevel@tonic-gate pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn)
3670Sstevel@tonic-gate {
3680Sstevel@tonic-gate 	unsigned char *fp;
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	PC_DPRINTF1(7, "pc_getcluster: cn=%x ", cn);
3710Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
3720Sstevel@tonic-gate 		panic("pc_getcluster");
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {	/* 32 bit FAT */
3750Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 2);
3760Sstevel@tonic-gate 		cn = ltohi(*(pc_cluster32_t *)fp);
3770Sstevel@tonic-gate 	} else if (fsp->pcfs_flags & PCFS_FAT16) {	/* 16 bit FAT */
3780Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 1);
3790Sstevel@tonic-gate 		cn = ltohs(*(pc_cluster16_t *)fp);
3800Sstevel@tonic-gate 	} else {	/* 12 bit FAT */
3810Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
3820Sstevel@tonic-gate 		if (cn & 01) {
3830Sstevel@tonic-gate 			cn = (((unsigned int)*fp++ & 0xf0) >> 4);
3840Sstevel@tonic-gate 			cn += (*fp << 4);
3850Sstevel@tonic-gate 		} else {
3860Sstevel@tonic-gate 			cn = *fp++;
3870Sstevel@tonic-gate 			cn += ((*fp & 0x0f) << 8);
3880Sstevel@tonic-gate 		}
3890Sstevel@tonic-gate 		if (cn >= PCF_12BCLUSTER)
3900Sstevel@tonic-gate 			cn |= PCF_RESCLUSTER;
3910Sstevel@tonic-gate 	}
3920Sstevel@tonic-gate 	PC_DPRINTF1(7, " %x\n", cn);
3930Sstevel@tonic-gate 	return (cn);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate  * Set a cluster in the FAT to a value.
3980Sstevel@tonic-gate  *	cn = cluster number to be set in FAT
3990Sstevel@tonic-gate  *	ncn = new value
4000Sstevel@tonic-gate  */
4010Sstevel@tonic-gate void
4020Sstevel@tonic-gate pc_setcluster(struct pcfs *fsp, pc_cluster32_t cn, pc_cluster32_t ncn)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	unsigned char *fp;
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	PC_DPRINTF2(7, "pc_setcluster: cn=%d ncn=%d\n", cn, ncn);
4070Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
4080Sstevel@tonic-gate 		panic("pc_setcluster");
4090Sstevel@tonic-gate 	fsp->pcfs_flags |= PCFS_FATMOD;
4100Sstevel@tonic-gate 	pc_mark_fat_updated(fsp, cn);
4110Sstevel@tonic-gate 	if (IS_FAT32(fsp)) {	/* 32 bit FAT */
4120Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 2);
4130Sstevel@tonic-gate 		*(pc_cluster32_t *)fp = htoli(ncn);
4140Sstevel@tonic-gate 	} else if (fsp->pcfs_flags & PCFS_FAT16) {	/* 16 bit FAT */
4150Sstevel@tonic-gate 		pc_cluster16_t	ncn16;
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn << 1);
4180Sstevel@tonic-gate 		ncn16 = (pc_cluster16_t)ncn;
4190Sstevel@tonic-gate 		*(pc_cluster16_t *)fp = htols(ncn16);
4200Sstevel@tonic-gate 	} else {	/* 12 bit FAT */
4210Sstevel@tonic-gate 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
4220Sstevel@tonic-gate 		if (cn & 01) {
4230Sstevel@tonic-gate 			*fp = (*fp & 0x0f) | ((ncn << 4) & 0xf0);
4240Sstevel@tonic-gate 			fp++;
4250Sstevel@tonic-gate 			*fp = (ncn >> 4) & 0xff;
4260Sstevel@tonic-gate 		} else {
4270Sstevel@tonic-gate 			*fp++ = ncn & 0xff;
4280Sstevel@tonic-gate 			*fp = (*fp & 0xf0) | ((ncn >> 8) & 0x0f);
4290Sstevel@tonic-gate 		}
4300Sstevel@tonic-gate 	}
4310Sstevel@tonic-gate 	if (ncn == PCF_FREECLUSTER) {
4320Sstevel@tonic-gate 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
4330Sstevel@tonic-gate 		if (IS_FAT32(fsp)) {
4340Sstevel@tonic-gate 			if (fsp->fsinfo_native.fs_free_clusters !=
4350Sstevel@tonic-gate 			    FSINFO_UNKNOWN)
4360Sstevel@tonic-gate 				fsp->fsinfo_native.fs_free_clusters++;
4370Sstevel@tonic-gate 		}
4380Sstevel@tonic-gate 	}
4390Sstevel@tonic-gate }
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate /*
4420Sstevel@tonic-gate  * Allocate a new cluster.
4430Sstevel@tonic-gate  */
4440Sstevel@tonic-gate pc_cluster32_t
4450Sstevel@tonic-gate pc_alloccluster(
4460Sstevel@tonic-gate 	struct pcfs *fsp,	/* file sys to allocate in */
4470Sstevel@tonic-gate 	int zwrite)			/* boolean for writing zeroes */
4480Sstevel@tonic-gate {
4490Sstevel@tonic-gate 	pc_cluster32_t cn;
4500Sstevel@tonic-gate 	int	error;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	if (fsp->pcfs_fatp == (uchar_t *)0)
4530Sstevel@tonic-gate 		panic("pc_addcluster: no FAT");
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	for (cn = fsp->pcfs_nxfrecls;
4562720Sfrankho 	    (int)cn < fsp->pcfs_ncluster; cn++) {
4570Sstevel@tonic-gate 		if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
4580Sstevel@tonic-gate 			struct buf *bp;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 			if (IS_FAT32(fsp)) {
4610Sstevel@tonic-gate 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK32);
4620Sstevel@tonic-gate 				if (fsp->fsinfo_native.fs_free_clusters !=
4630Sstevel@tonic-gate 				    FSINFO_UNKNOWN)
4640Sstevel@tonic-gate 					fsp->fsinfo_native.fs_free_clusters--;
4650Sstevel@tonic-gate 			} else
4660Sstevel@tonic-gate 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK);
4670Sstevel@tonic-gate 			if (zwrite) {
4680Sstevel@tonic-gate 				/*
4690Sstevel@tonic-gate 				 * zero the new cluster
4700Sstevel@tonic-gate 				 */
4710Sstevel@tonic-gate 				bp = ngeteblk(fsp->pcfs_clsize);
4720Sstevel@tonic-gate 				bp->b_edev = fsp->pcfs_xdev;
4730Sstevel@tonic-gate 				bp->b_dev = cmpdev(bp->b_edev);
4740Sstevel@tonic-gate 				bp->b_blkno = pc_cldaddr(fsp, cn);
4750Sstevel@tonic-gate 				clrbuf(bp);
4760Sstevel@tonic-gate 				bwrite2(bp);
4770Sstevel@tonic-gate 				error = geterror(bp);
4780Sstevel@tonic-gate 				brelse(bp);
4790Sstevel@tonic-gate 				if (error) {
4800Sstevel@tonic-gate 					PC_DPRINTF0(1,
4810Sstevel@tonic-gate 					    "pc_alloccluster: error\n");
4820Sstevel@tonic-gate 					pc_mark_irrecov(fsp);
4830Sstevel@tonic-gate 					return (PCF_ERRORCLUSTER);
4840Sstevel@tonic-gate 				}
4850Sstevel@tonic-gate 			}
4860Sstevel@tonic-gate 			fsp->pcfs_nxfrecls = cn + 1;
4870Sstevel@tonic-gate 			PC_DPRINTF1(5, "pc_alloccluster: new cluster = %d\n",
4880Sstevel@tonic-gate 			    cn);
4890Sstevel@tonic-gate 			return (cn);
4900Sstevel@tonic-gate 		}
4910Sstevel@tonic-gate 	}
4920Sstevel@tonic-gate 	return (PCF_FREECLUSTER);
4930Sstevel@tonic-gate }
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate /*
4960Sstevel@tonic-gate  * Get the number of clusters used by a file or subdirectory
4970Sstevel@tonic-gate  */
4980Sstevel@tonic-gate int
4990Sstevel@tonic-gate pc_fileclsize(
5000Sstevel@tonic-gate 	struct pcfs *fsp,
501*2972Sfrankho 	pc_cluster32_t startcl, pc_cluster32_t *ncl)
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate 	int count = 0;
5040Sstevel@tonic-gate 
505*2972Sfrankho 	*ncl = 0;
506*2972Sfrankho 	for (count = 0; pc_validcl(fsp, startcl);
507*2972Sfrankho 	    startcl = pc_getcluster(fsp, startcl)) {
508*2972Sfrankho 		if (count++ >= fsp->pcfs_ncluster)
509*2972Sfrankho 			return (EIO);
5100Sstevel@tonic-gate 	}
511*2972Sfrankho 	*ncl = (pc_cluster32_t)count;
512*2972Sfrankho 
513*2972Sfrankho 	return (0);
5140Sstevel@tonic-gate }
515