xref: /illumos-gate/usr/src/uts/common/sys/fs/pc_fs.h (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
50e42dee6Sartem  * Common Development and Distribution License (the "License").
60e42dee6Sartem  * 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 #ifndef	_SYS_FS_PC_FS_H
277c478bd9Sstevel@tonic-gate #define	_SYS_FS_PC_FS_H
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/thread.h>
30f127cb91Sfrankho #include <sys/ksynch.h>
31f127cb91Sfrankho #include <sys/sysmacros.h>
32f127cb91Sfrankho #include <sys/byteorder.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #ifdef	__cplusplus
357c478bd9Sstevel@tonic-gate extern "C" {
367c478bd9Sstevel@tonic-gate #endif
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate typedef	uint16_t	pc_cluster16_t;
397c478bd9Sstevel@tonic-gate typedef	uint32_t	pc_cluster32_t;
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * PC (MSDOS) compatible virtual file system.
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  * A main goal of the implementation was to maintain statelessness
457c478bd9Sstevel@tonic-gate  * except while files are open. Thus mounting and unmounting merely
467c478bd9Sstevel@tonic-gate  * declared the file system name. The user may change disks at almost
477c478bd9Sstevel@tonic-gate  * any time without concern (just like the PC). It is assumed that when
487c478bd9Sstevel@tonic-gate  * files are open for writing the disk access light will be on, as a
497c478bd9Sstevel@tonic-gate  * warning not to change disks. The implementation must, however, detect
507c478bd9Sstevel@tonic-gate  * disk change and recover gracefully. It does this by comparing the
517c478bd9Sstevel@tonic-gate  * in core entry for a directory to the on disk entry whenever a directory
527c478bd9Sstevel@tonic-gate  * is searched. If a discrepancy is found active directories become root and
537c478bd9Sstevel@tonic-gate  * active files are marked invalid.
547c478bd9Sstevel@tonic-gate  *
557c478bd9Sstevel@tonic-gate  * There are only two type of nodes on the PC file system; files and
567c478bd9Sstevel@tonic-gate  * directories. These are represented by two separate vnode op vectors,
577c478bd9Sstevel@tonic-gate  * and they are kept in two separate tables. Files are known by the
587c478bd9Sstevel@tonic-gate  * disk block number and block (cluster) offset of the files directory
597c478bd9Sstevel@tonic-gate  * entry. Directories are known by the starting cluster number.
607c478bd9Sstevel@tonic-gate  *
617c478bd9Sstevel@tonic-gate  * The file system is locked for during each user operation. This is
627c478bd9Sstevel@tonic-gate  * done to simplify disk verification error conditions.
637c478bd9Sstevel@tonic-gate  *
647c478bd9Sstevel@tonic-gate  * Notes on FAT32 support
657c478bd9Sstevel@tonic-gate  * ----------------------
667c478bd9Sstevel@tonic-gate  * The basic difference between FAT32 and FAT16 is that cluster numbers are now
677c478bd9Sstevel@tonic-gate  * 32-bit instead of 16-bit. The FAT is thus an array of 32-bit cluster numbers,
687c478bd9Sstevel@tonic-gate  * and because of this the cluster size can be much smaller on a large disk
697c478bd9Sstevel@tonic-gate  * (4k, say, on a 1 Gig drive instead of 16k). Unfortunately, the FAT is not
707c478bd9Sstevel@tonic-gate  * the only place cluster numbers are stored - the starting cluster is stored
717c478bd9Sstevel@tonic-gate  * in the directory entry for a file, and of course it's only 16-bit. Luckily,
727c478bd9Sstevel@tonic-gate  * there's a 16-bit OS/2 Extended Attribute field that is now used to store the
737c478bd9Sstevel@tonic-gate  * upper 16-bits of the starting cluster number.
747c478bd9Sstevel@tonic-gate  *
757c478bd9Sstevel@tonic-gate  * Most of the FAT32 changes to pcfs are under 'if it's FAT32' to minimize the
767c478bd9Sstevel@tonic-gate  * effect on non-FAT32 filesystems (and still share the code), except for the
777c478bd9Sstevel@tonic-gate  * starting cluster changes. It seemed easier to make common functions to
787c478bd9Sstevel@tonic-gate  * handle that.
797c478bd9Sstevel@tonic-gate  *
807c478bd9Sstevel@tonic-gate  * Other changes:
817c478bd9Sstevel@tonic-gate  *
827c478bd9Sstevel@tonic-gate  *     1. FAT32 partitions are indicated by partition types 0xB and 0xC.
837c478bd9Sstevel@tonic-gate  *     2. The boot sector is now 2 sectors, to make room for FAT32 extensions.
847c478bd9Sstevel@tonic-gate  *     3. The root directory is no longer stored in a fixed location. Its'
857c478bd9Sstevel@tonic-gate  *        starting cluster is stored in the extended boot sector.
867c478bd9Sstevel@tonic-gate  *     4. "Summary information" is now stored and we need to (at least) maintain
877c478bd9Sstevel@tonic-gate  *        the number of free clusters or scandisk will be upset. Though the
887c478bd9Sstevel@tonic-gate  *        sector this info is in is pointed to by the extensions in the boot
897c478bd9Sstevel@tonic-gate  *        sector, the magic offset of this information is just that so
907c478bd9Sstevel@tonic-gate  *        far - magic. 0x1e0.
917c478bd9Sstevel@tonic-gate  *     5. FAT32 can use the alternate FAT. But we don't.
927c478bd9Sstevel@tonic-gate  *
937c478bd9Sstevel@tonic-gate  * FAT32 also exposed a latent bug: we bread() each copy of the FAT in one
947c478bd9Sstevel@tonic-gate  * big chunk.  This is not good on a large FAT32 drive, such as a 1 Gig
957c478bd9Sstevel@tonic-gate  * Jaz drive that has 4k clusters, since the FAT becomes 1 Meg in size and
967c478bd9Sstevel@tonic-gate  * bread blocks forever. So now we read the FAT in chunks.
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate 
99f127cb91Sfrankho 
1007c478bd9Sstevel@tonic-gate /*
101f127cb91Sfrankho  * The FAT bootsector uses little-endian multibyte values not aligned at
102f127cb91Sfrankho  * a 'native' wordsize. Instead of defining a strange data structure and
103f127cb91Sfrankho  * odd accessor methods for some members while using standard C accesses
104f127cb91Sfrankho  * for others, we don't bother and just define the structure offsets, and
105f127cb91Sfrankho  * a common set of misaligned-littleendian accessor macros.
106f127cb91Sfrankho  *
107f127cb91Sfrankho  * The "bootsec" and "fat32_bootsec" structures are only provided for
108f127cb91Sfrankho  * compatibility with old code including <sys/fs/pc_fs.h> but not used
109f127cb91Sfrankho  * by the PCFS kernel driver anymore.
1107c478bd9Sstevel@tonic-gate  */
1117c478bd9Sstevel@tonic-gate struct bootsec {
1127c478bd9Sstevel@tonic-gate 	uchar_t	instr[3];
1137c478bd9Sstevel@tonic-gate 	uchar_t	version[8];
1147c478bd9Sstevel@tonic-gate 	uchar_t	bps[2];			/* bytes per sector */
1157c478bd9Sstevel@tonic-gate 	uchar_t	spcl;			/* sectors per allocation unit */
1167c478bd9Sstevel@tonic-gate 	uchar_t	res_sec[2];		/* reserved sectors, starting at 0 */
1177c478bd9Sstevel@tonic-gate 	uchar_t	nfat;			/* number of FATs */
1187c478bd9Sstevel@tonic-gate 	uchar_t	rdirents[2];		/* number of root directory entries */
1197c478bd9Sstevel@tonic-gate 	uchar_t	numsect[2];		/* old total sectors in logical image */
1207c478bd9Sstevel@tonic-gate 	uchar_t	mediadesriptor;		/* media descriptor byte */
1210e42dee6Sartem 	ushort_t fatsec;		/* number of sectors per FAT */
1227c478bd9Sstevel@tonic-gate 	ushort_t spt;			/* sectors per track */
1237c478bd9Sstevel@tonic-gate 	ushort_t nhead;			/* number of heads */
1240e42dee6Sartem 	uint_t	hiddensec;		/* number of hidden sectors */
1257c478bd9Sstevel@tonic-gate 	uint_t	totalsec;		/* total sectors in logical image */
1267c478bd9Sstevel@tonic-gate };
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate /*
1297c478bd9Sstevel@tonic-gate  * FAT32 volumes have a bigger boot sector. They include the normal
1307c478bd9Sstevel@tonic-gate  * boot sector.
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate struct fat32_bootsec {
1337c478bd9Sstevel@tonic-gate 	struct bootsec	f_bs;
1347c478bd9Sstevel@tonic-gate 	uint32_t	f_fatlength;	/* size of FAT */
1357c478bd9Sstevel@tonic-gate 	uint16_t	f_flags;
1367c478bd9Sstevel@tonic-gate 	uint8_t		f_major;	/* major filesystem version #? */
1377c478bd9Sstevel@tonic-gate 	uint8_t		f_minor;	/* minor filesystem version #? */
1387c478bd9Sstevel@tonic-gate 	uint32_t	f_rootcluster;	/* first cluster in root directory */
1397c478bd9Sstevel@tonic-gate 	uint16_t	f_infosector;	/* where summary info is */
1407c478bd9Sstevel@tonic-gate 	uint16_t	f_backupboot;	/* backup boot sector */
1417c478bd9Sstevel@tonic-gate 	uint16_t	f_reserved2[6];
1427c478bd9Sstevel@tonic-gate };
1437c478bd9Sstevel@tonic-gate 
144f127cb91Sfrankho 
145f127cb91Sfrankho #define	OFF_JMPBOOT	0
146f127cb91Sfrankho #define	OFF_OEMNAME	3
147f127cb91Sfrankho #define	OFF_BYTESPERSEC	11
148f127cb91Sfrankho #define	OFF_SECPERCLUS	13
149f127cb91Sfrankho #define	OFF_RSVDSECCNT	14
150f127cb91Sfrankho #define	OFF_NUMFATS	16
151f127cb91Sfrankho #define	OFF_ROOTENTCNT	17
152f127cb91Sfrankho #define	OFF_TOTSEC16	19
153f127cb91Sfrankho #define	OFF_MEDIA	21
154f127cb91Sfrankho #define	OFF_FATSZ16	22
155f127cb91Sfrankho #define	OFF_SECPERTRK	24
156f127cb91Sfrankho #define	OFF_NUMHEADS	26
157f127cb91Sfrankho #define	OFF_HIDDSEC	28
158f127cb91Sfrankho #define	OFF_TOTSEC32	32
159f127cb91Sfrankho #define	OFF_BPBSIG	510
160f127cb91Sfrankho 
161f127cb91Sfrankho #define	OFF_DRVNUM16	36
162f127cb91Sfrankho #define	OFF_BOOTSIG16	38
163f127cb91Sfrankho #define	OFF_VOLID16	39
164f127cb91Sfrankho #define	OFF_VOLLAB16	43
165f127cb91Sfrankho #define	OFF_FILSYSTYP16	54
166f127cb91Sfrankho 
167f127cb91Sfrankho #define	OFF_FATSZ32	36
168f127cb91Sfrankho #define	OFF_EXTFLAGS32	40
169f127cb91Sfrankho #define	OFF_FSVER32	42
170f127cb91Sfrankho #define	OFF_ROOTCLUS32	44
171f127cb91Sfrankho #define	OFF_FSINFO32	48
172f127cb91Sfrankho #define	OFF_BKBOOTSEC32	50
173f127cb91Sfrankho #define	OFF_DRVNUM32	64
174f127cb91Sfrankho #define	OFF_BOOTSIG32	66
175f127cb91Sfrankho #define	OFF_VOLID32	67
176f127cb91Sfrankho #define	OFF_VOLLAB32	71
177f127cb91Sfrankho #define	OFF_FILSYSTYP32	82
178f127cb91Sfrankho 
179f127cb91Sfrankho #define	LE_16_NA(addr)					\
180f127cb91Sfrankho 	(((uint16_t)*((uint8_t *)(addr))) +		\
181f127cb91Sfrankho 	((uint16_t)*((uint8_t *)(addr) + 1) << 8))
182f127cb91Sfrankho 
183f127cb91Sfrankho #define	LE_32_NA(addr)					\
184f127cb91Sfrankho 	(((uint32_t)*((uint8_t *)(addr))) +		\
185f127cb91Sfrankho 	((uint32_t)*((uint8_t *)(addr) + 1) << 8) +	\
186f127cb91Sfrankho 	((uint32_t)*((uint8_t *)(addr) + 2) << 16) +	\
187f127cb91Sfrankho 	((uint32_t)*((uint8_t *)(addr) + 3) << 24))
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate /*
190f127cb91Sfrankho  * Generic FAT BPB fields
1917c478bd9Sstevel@tonic-gate  */
192f127cb91Sfrankho #define	bpb_jmpBoot(bpb)		((unsigned char *)(bpb))
193f127cb91Sfrankho #define	bpb_OEMName(bpb)		((char *)(bpb) + OFF_OEMNAME)
194f127cb91Sfrankho #define	bpb_get_BytesPerSec(bpb)	LE_16_NA((bpb) + OFF_BYTESPERSEC)
195f127cb91Sfrankho #define	bpb_get_SecPerClus(bpb)		(((uint8_t *)(bpb))[OFF_SECPERCLUS])
196f127cb91Sfrankho #define	bpb_get_RsvdSecCnt(bpb)		LE_16_NA((bpb) + OFF_RSVDSECCNT)
197f127cb91Sfrankho #define	bpb_get_NumFATs(bpb)		(((uint8_t *)(bpb))[OFF_NUMFATS])
198f127cb91Sfrankho #define	bpb_get_RootEntCnt(bpb)		LE_16_NA((bpb) + OFF_ROOTENTCNT)
199f127cb91Sfrankho #define	bpb_get_TotSec16(bpb)		LE_16_NA((bpb) + OFF_TOTSEC16)
200f127cb91Sfrankho #define	bpb_get_Media(bpb)		(((uint8_t *)(bpb))[OFF_MEDIA])
201f127cb91Sfrankho #define	bpb_get_FatSz16(bpb)		LE_16_NA((bpb) + OFF_FATSZ16)
202f127cb91Sfrankho #define	bpb_get_SecPerTrk(bpb)		LE_16_NA((bpb) + OFF_SECPERTRK)
203f127cb91Sfrankho #define	bpb_get_NumHeads(bpb)		LE_16_NA((bpb) + OFF_NUMHEADS)
204f127cb91Sfrankho #define	bpb_get_HiddSec(bpb)		LE_32_NA((bpb) + OFF_HIDDSEC)
205f127cb91Sfrankho #define	bpb_get_TotSec32(bpb)		LE_32_NA((bpb) + OFF_TOTSEC32)
206f127cb91Sfrankho #define	bpb_get_BPBSig(bpb)		LE_16_NA((bpb) + OFF_BPBSIG)
207f127cb91Sfrankho 
208f127cb91Sfrankho /*
209f127cb91Sfrankho  * FAT12/16 extended BPB fields
210f127cb91Sfrankho  */
211f127cb91Sfrankho #define	bpb_get_DrvNum16(bpb)		(((uint8_t *)(bpb))[OFF_DRVNUM16])
212f127cb91Sfrankho #define	bpb_get_BootSig16(bpb)		(((uint8_t *)(bpb))[OFF_BOOTSIG16])
213f127cb91Sfrankho #define	bpb_VolLab16(bpb)		((char *)(bpb) + OFF_VOLLAB16)
214f127cb91Sfrankho #define	bpb_FilSysType16(bpb)		((char *)(bpb) + OFF_FILSYSTYP16)
215f127cb91Sfrankho #define	bpb_get_VolID16(bpb)		LE_32_NA((bpb) + OFF_VOLID16)
216f127cb91Sfrankho 
217f127cb91Sfrankho /*
218f127cb91Sfrankho  * FAT32 extended BPB fields
219f127cb91Sfrankho  */
220f127cb91Sfrankho #define	bpb_get_FatSz32(bpb)		LE_32_NA((bpb) + OFF_FATSZ32)
221f127cb91Sfrankho #define	bpb_get_ExtFlags32(bpb)		LE_16_NA((bpb) + OFF_EXTFLAGS32)
222f127cb91Sfrankho #define	bpb_get_FSVer32(bpb)		LE_16_NA((bpb) + OFF_FSVER32)
223f127cb91Sfrankho #define	bpb_get_RootClus32(bpb)		LE_32_NA((bpb) + OFF_ROOTCLUS32)
224f127cb91Sfrankho #define	bpb_get_FSInfo32(bpb)		LE_16_NA((bpb) + OFF_FSINFO32)
225f127cb91Sfrankho #define	bpb_get_BkBootSec32(bpb)	LE_16_NA((bpb) + OFF_BKBOOTSEC32)
226f127cb91Sfrankho #define	bpb_get_DrvNum32(bpb)		(((uint8_t *)(bpb))[OFF_DRVNUM32])
227f127cb91Sfrankho #define	bpb_get_BootSig32(bpb)		(((uint8_t *)(bpb))[OFF_BOOTSIG32])
228f127cb91Sfrankho #define	bpb_get_VolID32(bpb)		LE_32_NA((bpb) + OFF_VOLID32)
229f127cb91Sfrankho #define	bpb_VolLab32(bpb)		((char *)(bpb) + OFF_VOLLAB32)
230f127cb91Sfrankho #define	bpb_FilSysType32(bpb)		((char *)(bpb) + OFF_FILSYSTYP32)
231f127cb91Sfrankho 
232f127cb91Sfrankho /*
233f127cb91Sfrankho  * Validators
234f127cb91Sfrankho  */
235f127cb91Sfrankho #define	VALID_SECSIZE(s)	\
236f127cb91Sfrankho 	(s == 512 || s == 1024 || s == 2048 || s == 4096)
237f127cb91Sfrankho #define	VALID_SPCL(s)		(ISP2((s)) && (unsigned int)(s) <= 128)
238f127cb91Sfrankho #define	VALID_CLSIZE(s)		(ISP2((s)) && (unsigned int)(s) <= (64 * 1024))
239f127cb91Sfrankho #define	VALID_NUMFATS(n)	((n) > 0 && (n) < 8)
240f127cb91Sfrankho #define	VALID_RSVDSEC(s)	((s) > 0)
241f127cb91Sfrankho #define	VALID_BPBSIG(sig)	((sig) == MBB_MAGIC)
242f127cb91Sfrankho #define	VALID_BOOTSIG(sig)	((sig) == 0x29)
243f127cb91Sfrankho #define	VALID_MEDIA(m)		((m) == 0xF0 || ((m) >= 0xF8 && (m) <= 0xFF))
244f127cb91Sfrankho 
245f127cb91Sfrankho /*
246f127cb91Sfrankho  * this might require a change for codepage support. In particular,
247f127cb91Sfrankho  * pc_validchar() cannot be a macro anymore if codepages get involved.
248f127cb91Sfrankho  */
249f127cb91Sfrankho #define	VALID_VOLLAB(l)		(			\
250f127cb91Sfrankho 	pc_validchar((l)[0]) && pc_validchar((l)[1]) && \
251f127cb91Sfrankho 	pc_validchar((l)[2]) &&	pc_validchar((l)[3]) && \
252f127cb91Sfrankho 	pc_validchar((l)[4]) && pc_validchar((l)[5]) && \
253f127cb91Sfrankho 	pc_validchar((l)[6]) && pc_validchar((l)[7]) && \
254f127cb91Sfrankho 	pc_validchar((l)[8]) && pc_validchar((l)[9]) && \
255f127cb91Sfrankho 	pc_validchar((l)[10]))
256f127cb91Sfrankho 
257f127cb91Sfrankho /*
258f127cb91Sfrankho  * We might actually use the 'validchar' checks as well; it only needs
259f127cb91Sfrankho  * to be printable. Should this ever caused failed media recognition,
260f127cb91Sfrankho  * we can change it. Many ISVs put different strings into the "oemname"
261f127cb91Sfrankho  * field.
262f127cb91Sfrankho  */
263f127cb91Sfrankho #define	VALID_OEMNAME(nm)	(			\
264f127cb91Sfrankho 	bcmp((nm), "MSDOS", 5) == 0 || bcmp((nm), "MSWIN", 5) == 0)
265f127cb91Sfrankho #define	VALID_FSTYPSTR16(typ)	(bcmp((typ), "FAT", 3) == 0)
266f127cb91Sfrankho #define	VALID_FSTYPSTR32(typ)	(bcmp((typ), "FAT32", 5) == 0)
267f127cb91Sfrankho #define	VALID_JMPBOOT(b)	(			\
268f127cb91Sfrankho 	((b)[0] == 0xeb && (b)[2] == 0x90) || (b)[0] == 0xe9)
269f127cb91Sfrankho #define	VALID_FSVER32(v)	((v) == PCFS_SUPPORTED_FSVER)
270f127cb91Sfrankho /*
271f127cb91Sfrankho  * Can we check this properly somehow ? There should be a better way.
272f127cb91Sfrankho  * The FAT spec doesn't mention reserved bits need to be zero ...
273f127cb91Sfrankho  */
274f127cb91Sfrankho #define	VALID_EXTFLAGS(flags)	(((flags) & 0x8f) == (flags))
275f127cb91Sfrankho 
276f127cb91Sfrankho /*
277f127cb91Sfrankho  * Validation results
278f127cb91Sfrankho  */
279f127cb91Sfrankho #define	BPB_SECSIZE_OK		(1 << 0)	/* ok: 512/1024/2048/4096 */
280f127cb91Sfrankho #define	BPB_OEMNAME_OK		(1 << 1)	/* "MSDOS" or "MSWIN" */
281f127cb91Sfrankho #define	BPB_JMPBOOT_OK		(1 << 2)	/* 16bit "jmp" / "call" */
282f127cb91Sfrankho #define	BPB_SECPERCLUS_OK	(1 << 3)	/* power of 2, [1 .. 128] */
283f127cb91Sfrankho #define	BPB_RSVDSECCNT_OK	(1 << 4)	/* cannot be zero */
284f127cb91Sfrankho #define	BPB_NUMFAT_OK		(1 << 5)	/* >= 1, <= 8 */
285f127cb91Sfrankho #define	BPB_ROOTENTCNT_OK	(1 << 6)	/* 0 on FAT32, != 0 else */
286f127cb91Sfrankho #define	BPB_TOTSEC_OK		(1 << 7)	/* smaller than volume */
287f127cb91Sfrankho #define	BPB_TOTSEC16_OK		(1 << 8)	/* 0 on FAT32, != 0 on FAT12 */
288f127cb91Sfrankho #define	BPB_TOTSEC32_OK		(1 << 9)	/* 0 on FAT12, != 0 on FAT32 */
289f127cb91Sfrankho #define	BPB_MEDIADESC_OK	(1 << 10)	/* 0xf0 or 0xf8..0xff */
290f127cb91Sfrankho #define	BPB_FATSZ_OK		(1 << 11)	/* [nclusters], no smaller */
291f127cb91Sfrankho #define	BPB_FATSZ16_OK		(1 << 12)	/* 0 on FAT32, != 0 else */
292f127cb91Sfrankho #define	BPB_FATSZ32_OK		(1 << 13)	/* non-zero on FAT32 */
293f127cb91Sfrankho #define	BPB_BPBSIG_OK		(1 << 14)	/* 0x55, 0xAA */
294f127cb91Sfrankho #define	BPB_BOOTSIG16_OK	(1 << 15)	/* 0x29 - if present */
295f127cb91Sfrankho #define	BPB_BOOTSIG32_OK	(1 << 16)	/* 0x29 - unless SYSLINUX2.x */
296f127cb91Sfrankho #define	BPB_FSTYPSTR16_OK	(1 << 17)	/* At least "FAT" */
297f127cb91Sfrankho #define	BPB_FSTYPSTR32_OK	(1 << 18)	/* "FAT32" */
298f127cb91Sfrankho #define	BPB_EXTFLAGS_OK		(1 << 19)	/* reserved bits should be 0 */
299f127cb91Sfrankho #define	BPB_FSVER_OK		(1 << 20)	/* must be 0 */
300f127cb91Sfrankho #define	BPB_ROOTCLUSTER_OK	(1 << 21)	/* must be != 0 and valid */
301f127cb91Sfrankho #define	BPB_FSISEC_OK		(1 << 22)	/* != 0, <= reserved */
302f127cb91Sfrankho #define	BPB_BKBOOTSEC_OK	(1 << 23)	/* != 0, <= reserved, != fsi */
303f127cb91Sfrankho #define	BPB_VOLLAB16_OK		(1 << 24)	/* passes pc_validchar() */
304f127cb91Sfrankho #define	BPB_VOLLAB32_OK		(1 << 25)	/* passes pc_validchar() */
305f127cb91Sfrankho #define	BPB_NCLUSTERS_OK	(1 << 26)	/* from FAT spec */
306f127cb91Sfrankho #define	BPB_CLSIZE_OK		(1 << 27)	/* cluster size */
307f127cb91Sfrankho #define	BPB_MEDIASZ_OK		(1 << 28)	/* filesystem fits on device */
308f127cb91Sfrankho 
309f127cb91Sfrankho #define	FAT12_VALIDMSK							\
310f127cb91Sfrankho 	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
311f127cb91Sfrankho 	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
312f127cb91Sfrankho 	BPB_TOTSEC_OK | BPB_TOTSEC16_OK |				\
313f127cb91Sfrankho 	BPB_FATSZ_OK | BPB_FATSZ16_OK |	BPB_BPBSIG_OK)
314f127cb91Sfrankho 
315f127cb91Sfrankho #define	FAT16_VALIDMSK							\
316f127cb91Sfrankho 	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
317f127cb91Sfrankho 	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
318f127cb91Sfrankho 	BPB_TOTSEC_OK | BPB_TOTSEC16_OK | BPB_TOTSEC32_OK | 		\
319f127cb91Sfrankho 	BPB_FATSZ_OK | BPB_FATSZ16_OK | BPB_BPBSIG_OK)
320f127cb91Sfrankho 
321f127cb91Sfrankho /*
322f127cb91Sfrankho  * A note on FAT32: According to the FAT spec, FAT32 _must_ have a valid
323f127cb91Sfrankho  * extended BPB and therefore, as a proof of its existance, the FAT32
324f127cb91Sfrankho  * boot signature (offset 66) must be valid as well. Why don't we check
325f127cb91Sfrankho  * for BPB_BOOTSIG32_OK  then ?
326f127cb91Sfrankho  *
327f127cb91Sfrankho  * We don't test for this here first-pass, because there are media out
328f127cb91Sfrankho  * there that are valid FAT32 structurally but don't have a valid sig.
329f127cb91Sfrankho  * This happens if older versions of the SYSLINUX bootloader (below 3.x)
330f127cb91Sfrankho  * are installed on a media with a FAT32 on it. SYSLINUX 2.x and lower
331f127cb91Sfrankho  * overwrite the BPB past the end of the FAT12/16 extension with its
332f127cb91Sfrankho  * bootloader code - and the FAT16 extended BPB is 62 Bytes...
333f127cb91Sfrankho  * All structurally relevant fields of the FAT32 BPB are within the first
334f127cb91Sfrankho  * 52 Bytes, so the filesystem is accessible - but the signature check
335f127cb91Sfrankho  * would reject it.
336f127cb91Sfrankho  */
337f127cb91Sfrankho #define	FAT32_VALIDMSK							\
338f127cb91Sfrankho 	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
339f127cb91Sfrankho 	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
340f127cb91Sfrankho 	BPB_TOTSEC_OK | BPB_TOTSEC16_OK | BPB_TOTSEC32_OK | 		\
341f127cb91Sfrankho 	BPB_FATSZ_OK | BPB_FATSZ16_OK |	BPB_FATSZ32_OK |		\
342f127cb91Sfrankho 	BPB_EXTFLAGS_OK | BPB_FSVER_OK | BPB_ROOTCLUSTER_OK |		\
343f127cb91Sfrankho 	BPB_BPBSIG_OK)
344f127cb91Sfrankho 
345f127cb91Sfrankho /*
346f127cb91Sfrankho  * FAT32 BPB allows 'versioning' via FSVer32. We follow the 'NULL' spec.
347f127cb91Sfrankho  */
348f127cb91Sfrankho #define	PCFS_SUPPORTED_FSVER	0
349f127cb91Sfrankho 
350f127cb91Sfrankho 
351f127cb91Sfrankho /*
352f127cb91Sfrankho  * Filesystem summary information (introduced originally for FAT32 volumes).
353f127cb91Sfrankho  * We need to maintain fs_free_clusters or Microsoft Scandisk will be upset.
354f127cb91Sfrankho  * We keep these values in-core even for FAT12/FAT16 but will never attempt
355f127cb91Sfrankho  * to write them out to disk then.
356f127cb91Sfrankho  */
357f127cb91Sfrankho typedef struct fat_fsinfo {
358f127cb91Sfrankho 	uint32_t fs_free_clusters;	/* # free clusters. -1 if unknown */
359f127cb91Sfrankho 	uint32_t fs_next_free;		/* search next free after this cn */
360f127cb91Sfrankho } fat_fsi_t;
361f127cb91Sfrankho 
362f127cb91Sfrankho /*
363f127cb91Sfrankho  * On-disk FSI. All values in little endian. Only FAT32 has this.
364f127cb91Sfrankho  */
365f127cb91Sfrankho typedef struct fat_od_fsi {
366f127cb91Sfrankho 	uint32_t	fsi_leadsig;		/* 0x41615252 */
367f127cb91Sfrankho 	char		fsi_reserved1[480];
368f127cb91Sfrankho 	uint32_t	fsi_strucsig;		/* 0x61417272 */
369f127cb91Sfrankho 	fat_fsi_t	fsi_incore;		/* free/nextfree */
370f127cb91Sfrankho 	char		fsi_reserved2[12];
371f127cb91Sfrankho 	uint32_t	fsi_trailsig;		/* 0xaa550000 */
372f127cb91Sfrankho } fat_od_fsi_t;
373f127cb91Sfrankho 
374f127cb91Sfrankho #define	FSI_LEADSIG	LE_32(0x41615252)
375f127cb91Sfrankho #define	FSI_STRUCSIG	LE_32(0x61417272)
376f127cb91Sfrankho #define	FSI_TRAILSIG	LE_32(0xaa550000)	/* same as MBB_MAGIC */
377f127cb91Sfrankho 
378f127cb91Sfrankho #define	FSISIG_OK(fsi)	(						\
379f127cb91Sfrankho 	((fat_od_fsi_t *)(fsi))->fsi_leadsig == FSI_LEADSIG &&		\
380f127cb91Sfrankho 	((fat_od_fsi_t *)(fsi))->fsi_strucsig == FSI_STRUCSIG &&	\
381f127cb91Sfrankho 	((fat_od_fsi_t *)(fsi))->fsi_trailsig == FSI_TRAILSIG)
382f127cb91Sfrankho 
383f127cb91Sfrankho #define	FSINFO_UNKNOWN	((uint32_t)(-1))	/* free/next not valid */
384f127cb91Sfrankho 
385f127cb91Sfrankho typedef enum { FAT12, FAT16, FAT32, FAT_UNKNOWN, FAT_QUESTIONABLE } fattype_t;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate struct pcfs {
3897c478bd9Sstevel@tonic-gate 	struct vfs *pcfs_vfs;		/* vfs for this fs */
3907c478bd9Sstevel@tonic-gate 	int pcfs_flags;			/* flags */
391f127cb91Sfrankho 	int pcfs_ldrive;		/* logical DOS drive number */
392f127cb91Sfrankho 	fattype_t pcfs_fattype;
3937c478bd9Sstevel@tonic-gate 	dev_t pcfs_xdev;		/* actual device that is mounted */
3947c478bd9Sstevel@tonic-gate 	struct vnode *pcfs_devvp;	/*   and a vnode for it */
3957c478bd9Sstevel@tonic-gate 	int pcfs_secsize;		/* sector size in bytes */
3967c478bd9Sstevel@tonic-gate 	int pcfs_spcl;			/* sectors per cluster */
3977c478bd9Sstevel@tonic-gate 	int pcfs_spt;			/* sectors per track */
3987c478bd9Sstevel@tonic-gate 	int pcfs_sdshift;		/* shift to convert sector into */
3997c478bd9Sstevel@tonic-gate 					/* DEV_BSIZE "sectors"; assume */
4007c478bd9Sstevel@tonic-gate 					/* pcfs_secsize is 2**n times of */
4017c478bd9Sstevel@tonic-gate 					/* DEV_BSIZE */
4027c478bd9Sstevel@tonic-gate 	int pcfs_fatsec;		/* number of sec per FAT */
4037c478bd9Sstevel@tonic-gate 	int pcfs_numfat;		/* number of FAT copies */
4047c478bd9Sstevel@tonic-gate 	int pcfs_rdirsec;		/* number of sec in root dir */
4057c478bd9Sstevel@tonic-gate 	daddr_t pcfs_dosstart;		/* start blkno of DOS partition */
406f127cb91Sfrankho 	daddr_t pcfs_fsistart;		/* start blkno of FSI sector */
4077c478bd9Sstevel@tonic-gate 	daddr_t pcfs_fatstart;		/* start blkno of first FAT */
4087c478bd9Sstevel@tonic-gate 	daddr_t pcfs_rdirstart;		/* start blkno of root dir */
4097c478bd9Sstevel@tonic-gate 	daddr_t pcfs_datastart;		/* start blkno of data area */
4107c478bd9Sstevel@tonic-gate 	int pcfs_clsize;		/* cluster size in bytes */
4117c478bd9Sstevel@tonic-gate 	int pcfs_ncluster;		/* number of clusters in fs */
4127c478bd9Sstevel@tonic-gate 	int pcfs_nrefs;			/* number of active pcnodes */
4137c478bd9Sstevel@tonic-gate 	int pcfs_frefs;			/* number of active file pcnodes */
4147c478bd9Sstevel@tonic-gate 	int pcfs_nxfrecls;		/* next free cluster */
4157c478bd9Sstevel@tonic-gate 	uchar_t *pcfs_fatp;		/* ptr to FAT data */
4167c478bd9Sstevel@tonic-gate 	uchar_t *pcfs_fat_changemap;	/* map of changed fat data */
4177c478bd9Sstevel@tonic-gate 	int pcfs_fat_changemapsize;	/* size of FAT changemap */
4187c478bd9Sstevel@tonic-gate 	time_t pcfs_fattime;		/* time FAT becomes invalid */
4197c478bd9Sstevel@tonic-gate 	time_t pcfs_verifytime;		/* time to reverify disk */
4207c478bd9Sstevel@tonic-gate 	kmutex_t	pcfs_lock;		/* per filesystem lock */
4217c478bd9Sstevel@tonic-gate 	kthread_id_t pcfs_owner;		/* id of thread locking pcfs */
4227c478bd9Sstevel@tonic-gate 	int pcfs_count;			/* # of pcfs locks for pcfs_owner */
423f127cb91Sfrankho 	struct fat_fsinfo pcfs_fsinfo;	/* in-core fsinfo */
4247c478bd9Sstevel@tonic-gate 	struct pcfs *pcfs_nxt;		/* linked list of all mounts */
4257c478bd9Sstevel@tonic-gate 	int pcfs_fatjustread;		/* Used to flag a freshly found FAT */
426264a6e74Sfrankho 	struct vnode *pcfs_root;	/* vnode for the root dir of the fs */
427f127cb91Sfrankho 	int pcfs_secondswest;		/* recording timezone for this fs */
428f127cb91Sfrankho 	len_t pcfs_mediasize;
429f127cb91Sfrankho 	int pcfs_rootblksize;
430f127cb91Sfrankho 	int pcfs_mediadesc;		/* media descriptor */
431f127cb91Sfrankho 	pc_cluster32_t pcfs_lastclmark;
432f127cb91Sfrankho 	pc_cluster32_t pcfs_rootclnum;
433f127cb91Sfrankho 	timestruc_t pcfs_mounttime;	/* timestamp for "/" */
4347c478bd9Sstevel@tonic-gate };
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate /*
4377c478bd9Sstevel@tonic-gate  * flags
4387c478bd9Sstevel@tonic-gate  */
439264a6e74Sfrankho #define	PCFS_FATMOD		0x01	/* FAT has been modified */
440264a6e74Sfrankho #define	PCFS_LOCKED		0x02	/* fs is locked */
441264a6e74Sfrankho #define	PCFS_WANTED		0x04	/* locked fs is wanted */
442264a6e74Sfrankho #define	PCFS_NOCHK		0x800	/* don't resync fat on error */
443264a6e74Sfrankho #define	PCFS_BOOTPART		0x1000	/* boot partition type */
444264a6e74Sfrankho #define	PCFS_HIDDEN		0x2000	/* show hidden files */
445264a6e74Sfrankho #define	PCFS_PCMCIA_NO_CIS	0x4000	/* PCMCIA psuedo floppy */
446264a6e74Sfrankho #define	PCFS_FOLDCASE		0x8000	/* fold filenames to lowercase */
447f127cb91Sfrankho #define	PCFS_FSINFO_OK		0x10000	/* valid FAT32 fsinfo sector */
448264a6e74Sfrankho #define	PCFS_IRRECOV		0x20000	/* FS was messed with during write */
449264a6e74Sfrankho #define	PCFS_NOCLAMPTIME	0x40000	/* expose full FAT timestamp range */
450f127cb91Sfrankho #define	PCFS_NOATIME		0x80000	/* disable atime updates */
451f127cb91Sfrankho 
452f127cb91Sfrankho #define	IS_FAT12(PCFS)	((PCFS)->pcfs_fattype == FAT12)
453f127cb91Sfrankho #define	IS_FAT16(PCFS)	((PCFS)->pcfs_fattype == FAT16)
454f127cb91Sfrankho #define	IS_FAT32(PCFS)	((PCFS)->pcfs_fattype == FAT32)
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate /* for compatibility */
4577c478bd9Sstevel@tonic-gate struct old_pcfs_args {
4587c478bd9Sstevel@tonic-gate 	int	secondswest;	/* seconds west of Greenwich */
4597c478bd9Sstevel@tonic-gate 	int	dsttime;    	/* type of dst correction */
4607c478bd9Sstevel@tonic-gate