summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/sys/fs/pc_fs.h
blob: d585003493e50bcf80131e11bbd1f146b92d047e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_SYS_FS_PC_FS_H
#define	_SYS_FS_PC_FS_H

#include <sys/thread.h>
#include <sys/ksynch.h>
#include <sys/sysmacros.h>
#include <sys/byteorder.h>

#ifdef	__cplusplus
extern "C" {
#endif

typedef	uint16_t	pc_cluster16_t;
typedef	uint32_t	pc_cluster32_t;

/*
 * PC (MSDOS) compatible virtual file system.
 *
 * A main goal of the implementation was to maintain statelessness
 * except while files are open. Thus mounting and unmounting merely
 * declared the file system name. The user may change disks at almost
 * any time without concern (just like the PC). It is assumed that when
 * files are open for writing the disk access light will be on, as a
 * warning not to change disks. The implementation must, however, detect
 * disk change and recover gracefully. It does this by comparing the
 * in core entry for a directory to the on disk entry whenever a directory
 * is searched. If a discrepancy is found active directories become root and
 * active files are marked invalid.
 *
 * There are only two type of nodes on the PC file system; files and
 * directories. These are represented by two separate vnode op vectors,
 * and they are kept in two separate tables. Files are known by the
 * disk block number and block (cluster) offset of the files directory
 * entry. Directories are known by the starting cluster number.
 *
 * The file system is locked for during each user operation. This is
 * done to simplify disk verification error conditions.
 *
 * Notes on FAT32 support
 * ----------------------
 * The basic difference between FAT32 and FAT16 is that cluster numbers are now
 * 32-bit instead of 16-bit. The FAT is thus an array of 32-bit cluster numbers,
 * and because of this the cluster size can be much smaller on a large disk
 * (4k, say, on a 1 Gig drive instead of 16k). Unfortunately, the FAT is not
 * the only place cluster numbers are stored - the starting cluster is stored
 * in the directory entry for a file, and of course it's only 16-bit. Luckily,
 * there's a 16-bit OS/2 Extended Attribute field that is now used to store the
 * upper 16-bits of the starting cluster number.
 *
 * Most of the FAT32 changes to pcfs are under 'if it's FAT32' to minimize the
 * effect on non-FAT32 filesystems (and still share the code), except for the
 * starting cluster changes. It seemed easier to make common functions to
 * handle that.
 *
 * Other changes:
 *
 *     1. FAT32 partitions are indicated by partition types 0xB and 0xC.
 *     2. The boot sector is now 2 sectors, to make room for FAT32 extensions.
 *     3. The root directory is no longer stored in a fixed location. Its'
 *        starting cluster is stored in the extended boot sector.
 *     4. "Summary information" is now stored and we need to (at least) maintain
 *        the number of free clusters or scandisk will be upset. Though the
 *        sector this info is in is pointed to by the extensions in the boot
 *        sector, the magic offset of this information is just that so
 *        far - magic. 0x1e0.
 *     5. FAT32 can use the alternate FAT. But we don't.
 *
 * FAT32 also exposed a latent bug: we bread() each copy of the FAT in one
 * big chunk.  This is not good on a large FAT32 drive, such as a 1 Gig
 * Jaz drive that has 4k clusters, since the FAT becomes 1 Meg in size and
 * bread blocks forever. So now we read the FAT in chunks.
 */


/*
 * The FAT bootsector uses little-endian multibyte values not aligned at
 * a 'native' wordsize. Instead of defining a strange data structure and
 * odd accessor methods for some members while using standard C accesses
 * for others, we don't bother and just define the structure offsets, and
 * a common set of misaligned-littleendian accessor macros.
 *
 * The "bootsec" and "fat32_bootsec" structures are only provided for
 * compatibility with old code including <sys/fs/pc_fs.h> but not used
 * by the PCFS kernel driver anymore.
 */
struct bootsec {
	uchar_t	instr[3];
	uchar_t	version[8];
	uchar_t	bps[2];			/* bytes per sector */
	uchar_t	spcl;			/* sectors per allocation unit */
	uchar_t	res_sec[2];		/* reserved sectors, starting at 0 */
	uchar_t	nfat;			/* number of FATs */
	uchar_t	rdirents[2];		/* number of root directory entries */
	uchar_t	numsect[2];		/* old total sectors in logical image */
	uchar_t	mediadesriptor;		/* media descriptor byte */
	ushort_t fatsec;		/* number of sectors per FAT */
	ushort_t spt;			/* sectors per track */
	ushort_t nhead;			/* number of heads */
	uint_t	hiddensec;		/* number of hidden sectors */
	uint_t	totalsec;		/* total sectors in logical image */
};

/*
 * FAT32 volumes have a bigger boot sector. They include the normal
 * boot sector.
 */
struct fat32_bootsec {
	struct bootsec	f_bs;
	uint32_t	f_fatlength;	/* size of FAT */
	uint16_t	f_flags;
	uint8_t		f_major;	/* major filesystem version #? */
	uint8_t		f_minor;	/* minor filesystem version #? */
	uint32_t	f_rootcluster;	/* first cluster in root directory */
	uint16_t	f_infosector;	/* where summary info is */
	uint16_t	f_backupboot;	/* backup boot sector */
	uint16_t	f_reserved2[6];
};


#define	OFF_JMPBOOT	0
#define	OFF_OEMNAME	3
#define	OFF_BYTESPERSEC	11
#define	OFF_SECPERCLUS	13
#define	OFF_RSVDSECCNT	14
#define	OFF_NUMFATS	16
#define	OFF_ROOTENTCNT	17
#define	OFF_TOTSEC16	19
#define	OFF_MEDIA	21
#define	OFF_FATSZ16	22
#define	OFF_SECPERTRK	24
#define	OFF_NUMHEADS	26
#define	OFF_HIDDSEC	28
#define	OFF_TOTSEC32	32
#define	OFF_BPBSIG	510

#define	OFF_DRVNUM16	36
#define	OFF_BOOTSIG16	38
#define	OFF_VOLID16	39
#define	OFF_VOLLAB16	43
#define	OFF_FILSYSTYP16	54

#define	OFF_FATSZ32	36
#define	OFF_EXTFLAGS32	40
#define	OFF_FSVER32	42
#define	OFF_ROOTCLUS32	44
#define	OFF_FSINFO32	48
#define	OFF_BKBOOTSEC32	50
#define	OFF_DRVNUM32	64
#define	OFF_BOOTSIG32	66
#define	OFF_VOLID32	67
#define	OFF_VOLLAB32	71
#define	OFF_FILSYSTYP32	82

#define	LE_16_NA(addr)					\
	(((uint16_t)*((uint8_t *)(addr))) +		\
	((uint16_t)*((uint8_t *)(addr) + 1) << 8))

#define	LE_32_NA(addr)					\
	(((uint32_t)*((uint8_t *)(addr))) +		\
	((uint32_t)*((uint8_t *)(addr) + 1) << 8) +	\
	((uint32_t)*((uint8_t *)(addr) + 2) << 16) +	\
	((uint32_t)*((uint8_t *)(addr) + 3) << 24))

/*
 * Generic FAT BPB fields
 */
#define	bpb_jmpBoot(bpb)		((unsigned char *)(bpb))
#define	bpb_OEMName(bpb)		((char *)(bpb) + OFF_OEMNAME)
#define	bpb_get_BytesPerSec(bpb)	LE_16_NA((bpb) + OFF_BYTESPERSEC)
#define	bpb_get_SecPerClus(bpb)		(((uint8_t *)(bpb))[OFF_SECPERCLUS])
#define	bpb_get_RsvdSecCnt(bpb)		LE_16_NA((bpb) + OFF_RSVDSECCNT)
#define	bpb_get_NumFATs(bpb)		(((uint8_t *)(bpb))[OFF_NUMFATS])
#define	bpb_get_RootEntCnt(bpb)		LE_16_NA((bpb) + OFF_ROOTENTCNT)
#define	bpb_get_TotSec16(bpb)		LE_16_NA((bpb) + OFF_TOTSEC16)
#define	bpb_get_Media(bpb)		(((uint8_t *)(bpb))[OFF_MEDIA])
#define	bpb_get_FatSz16(bpb)		LE_16_NA((bpb) + OFF_FATSZ16)
#define	bpb_get_SecPerTrk(bpb)		LE_16_NA((bpb) + OFF_SECPERTRK)
#define	bpb_get_NumHeads(bpb)		LE_16_NA((bpb) + OFF_NUMHEADS)
#define	bpb_get_HiddSec(bpb)		LE_32_NA((bpb) + OFF_HIDDSEC)
#define	bpb_get_TotSec32(bpb)		LE_32_NA((bpb) + OFF_TOTSEC32)
#define	bpb_get_BPBSig(bpb)		LE_16_NA((bpb) + OFF_BPBSIG)

/*
 * FAT12/16 extended BPB fields
 */
#define	bpb_get_DrvNum16(bpb)		(((uint8_t *)(bpb))[OFF_DRVNUM16])
#define	bpb_get_BootSig16(bpb)		(((uint8_t *)(bpb))[OFF_BOOTSIG16])
#define	bpb_VolLab16(bpb)		((char *)(bpb) + OFF_VOLLAB16)
#define	bpb_FilSysType16(bpb)		((char *)(bpb) + OFF_FILSYSTYP16)
#define	bpb_get_VolID16(bpb)		LE_32_NA((bpb) + OFF_VOLID16)

/*
 * FAT32 extended BPB fields
 */
#define	bpb_get_FatSz32(bpb)		LE_32_NA((bpb) + OFF_FATSZ32)
#define	bpb_get_ExtFlags32(bpb)		LE_16_NA((bpb) + OFF_EXTFLAGS32)
#define	bpb_get_FSVer32(bpb)		LE_16_NA((bpb) + OFF_FSVER32)
#define	bpb_get_RootClus32(bpb)		LE_32_NA((bpb) + OFF_ROOTCLUS32)
#define	bpb_get_FSInfo32(bpb)		LE_16_NA((bpb) + OFF_FSINFO32)
#define	bpb_get_BkBootSec32(bpb)	LE_16_NA((bpb) + OFF_BKBOOTSEC32)
#define	bpb_get_DrvNum32(bpb)		(((uint8_t *)(bpb))[OFF_DRVNUM32])
#define	bpb_get_BootSig32(bpb)		(((uint8_t *)(bpb))[OFF_BOOTSIG32])
#define	bpb_get_VolID32(bpb)		LE_32_NA((bpb) + OFF_VOLID32)
#define	bpb_VolLab32(bpb)		((char *)(bpb) + OFF_VOLLAB32)
#define	bpb_FilSysType32(bpb)		((char *)(bpb) + OFF_FILSYSTYP32)

/*
 * Validators
 */
#define	VALID_SECSIZE(s)	\
	(s == 512 || s == 1024 || s == 2048 || s == 4096)
#define	VALID_SPCL(s)		(ISP2((s)) && (unsigned int)(s) <= 128)
#define	VALID_CLSIZE(s)		(ISP2((s)) && (unsigned int)(s) <= (64 * 1024))
#define	VALID_NUMFATS(n)	((n) > 0 && (n) < 8)
#define	VALID_RSVDSEC(s)	((s) > 0)
#define	VALID_BPBSIG(sig)	((sig) == MBB_MAGIC)
#define	VALID_BOOTSIG(sig)	((sig) == 0x29)
#define	VALID_MEDIA(m)		((m) == 0xF0 || ((m) >= 0xF8 && (m) <= 0xFF))

/*
 * this might require a change for codepage support. In particular,
 * pc_validchar() cannot be a macro anymore if codepages get involved.
 */
#define	VALID_VOLLAB(l)		(			\
	pc_validchar((l)[0]) && pc_validchar((l)[1]) && \
	pc_validchar((l)[2]) &&	pc_validchar((l)[3]) && \
	pc_validchar((l)[4]) && pc_validchar((l)[5]) && \
	pc_validchar((l)[6]) && pc_validchar((l)[7]) && \
	pc_validchar((l)[8]) && pc_validchar((l)[9]) && \
	pc_validchar((l)[10]))

/*
 * We might actually use the 'validchar' checks as well; it only needs
 * to be printable. Should this ever caused failed media recognition,
 * we can change it. Many ISVs put different strings into the "oemname"
 * field.
 */
#define	VALID_OEMNAME(nm)	(			\
	bcmp((nm), "MSDOS", 5) == 0 || bcmp((nm), "MSWIN", 5) == 0)
#define	VALID_FSTYPSTR16(typ)	(bcmp((typ), "FAT", 3) == 0)
#define	VALID_FSTYPSTR32(typ)	(bcmp((typ), "FAT32", 5) == 0)
#define	VALID_JMPBOOT(b)	(			\
	((b)[0] == 0xeb && (b)[2] == 0x90) || (b)[0] == 0xe9)
#define	VALID_FSVER32(v)	((v) == PCFS_SUPPORTED_FSVER)
/*
 * Can we check this properly somehow ? There should be a better way.
 * The FAT spec doesn't mention reserved bits need to be zero ...
 */
#define	VALID_EXTFLAGS(flags)	(((flags) & 0x8f) == (flags))

/*
 * Validation results
 */
#define	BPB_SECSIZE_OK		(1 << 0)	/* ok: 512/1024/2048/4096 */
#define	BPB_OEMNAME_OK		(1 << 1)	/* "MSDOS" or "MSWIN" */
#define	BPB_JMPBOOT_OK		(1 << 2)	/* 16bit "jmp" / "call" */
#define	BPB_SECPERCLUS_OK	(1 << 3)	/* power of 2, [1 .. 128] */
#define	BPB_RSVDSECCNT_OK	(1 << 4)	/* cannot be zero */
#define	BPB_NUMFAT_OK		(1 << 5)	/* >= 1, <= 8 */
#define	BPB_ROOTENTCNT_OK	(1 << 6)	/* 0 on FAT32, != 0 else */
#define	BPB_TOTSEC_OK		(1 << 7)	/* smaller than volume */
#define	BPB_TOTSEC16_OK		(1 << 8)	/* 0 on FAT32, != 0 on FAT12 */
#define	BPB_TOTSEC32_OK		(1 << 9)	/* 0 on FAT12, != 0 on FAT32 */
#define	BPB_MEDIADESC_OK	(1 << 10)	/* 0xf0 or 0xf8..0xff */
#define	BPB_FATSZ_OK		(1 << 11)	/* [nclusters], no smaller */
#define	BPB_FATSZ16_OK		(1 << 12)	/* 0 on FAT32, != 0 else */
#define	BPB_FATSZ32_OK		(1 << 13)	/* non-zero on FAT32 */
#define	BPB_BPBSIG_OK		(1 << 14)	/* 0x55, 0xAA */
#define	BPB_BOOTSIG16_OK	(1 << 15)	/* 0x29 - if present */
#define	BPB_BOOTSIG32_OK	(1 << 16)	/* 0x29 - unless SYSLINUX2.x */
#define	BPB_FSTYPSTR16_OK	(1 << 17)	/* At least "FAT" */
#define	BPB_FSTYPSTR32_OK	(1 << 18)	/* "FAT32" */
#define	BPB_EXTFLAGS_OK		(1 << 19)	/* reserved bits should be 0 */
#define	BPB_FSVER_OK		(1 << 20)	/* must be 0 */
#define	BPB_ROOTCLUSTER_OK	(1 << 21)	/* must be != 0 and valid */
#define	BPB_FSISEC_OK		(1 << 22)	/* != 0, <= reserved */
#define	BPB_BKBOOTSEC_OK	(1 << 23)	/* != 0, <= reserved, != fsi */
#define	BPB_VOLLAB16_OK		(1 << 24)	/* passes pc_validchar() */
#define	BPB_VOLLAB32_OK		(1 << 25)	/* passes pc_validchar() */
#define	BPB_NCLUSTERS_OK	(1 << 26)	/* from FAT spec */
#define	BPB_CLSIZE_OK		(1 << 27)	/* cluster size */
#define	BPB_MEDIASZ_OK		(1 << 28)	/* filesystem fits on device */

#define	FAT12_VALIDMSK							\
	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
	BPB_TOTSEC_OK | BPB_TOTSEC16_OK |				\
	BPB_FATSZ_OK | BPB_FATSZ16_OK |	BPB_BPBSIG_OK)

#define	FAT16_VALIDMSK							\
	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
	BPB_TOTSEC_OK | BPB_TOTSEC16_OK | BPB_TOTSEC32_OK | 		\
	BPB_FATSZ_OK | BPB_FATSZ16_OK | BPB_BPBSIG_OK)

/*
 * A note on FAT32: According to the FAT spec, FAT32 _must_ have a valid
 * extended BPB and therefore, as a proof of its existance, the FAT32
 * boot signature (offset 66) must be valid as well. Why don't we check
 * for BPB_BOOTSIG32_OK  then ?
 *
 * We don't test for this here first-pass, because there are media out
 * there that are valid FAT32 structurally but don't have a valid sig.
 * This happens if older versions of the SYSLINUX bootloader (below 3.x)
 * are installed on a media with a FAT32 on it. SYSLINUX 2.x and lower
 * overwrite the BPB past the end of the FAT12/16 extension with its
 * bootloader code - and the FAT16 extended BPB is 62 Bytes...
 * All structurally relevant fields of the FAT32 BPB are within the first
 * 52 Bytes, so the filesystem is accessible - but the signature check
 * would reject it.
 */
#define	FAT32_VALIDMSK							\
	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
	BPB_TOTSEC_OK | BPB_TOTSEC16_OK | BPB_TOTSEC32_OK | 		\
	BPB_FATSZ_OK | BPB_FATSZ16_OK |	BPB_FATSZ32_OK |		\
	BPB_EXTFLAGS_OK | BPB_FSVER_OK | BPB_ROOTCLUSTER_OK |		\
	BPB_BPBSIG_OK)

/*
 * FAT32 BPB allows 'versioning' via FSVer32. We follow the 'NULL' spec.
 */
#define	PCFS_SUPPORTED_FSVER	0


/*
 * Filesystem summary information (introduced originally for FAT32 volumes).
 * We need to maintain fs_free_clusters or Microsoft Scandisk will be upset.
 * We keep these values in-core even for FAT12/FAT16 but will never attempt
 * to write them out to disk then.
 */
typedef struct fat_fsinfo {
	uint32_t fs_free_clusters;	/* # free clusters. -1 if unknown */
	uint32_t fs_next_free;		/* search next free after this cn */
} fat_fsi_t;

/*
 * On-disk FSI. All values in little endian. Only FAT32 has this.
 */
typedef struct fat_od_fsi {
	uint32_t	fsi_leadsig;		/* 0x41615252 */
	char		fsi_reserved1[480];
	uint32_t	fsi_strucsig;		/* 0x61417272 */
	fat_fsi_t	fsi_incore;		/* free/nextfree */
	char		fsi_reserved2[12];
	uint32_t	fsi_trailsig;		/* 0xaa550000 */
} fat_od_fsi_t;

#define	FSI_LEADSIG	LE_32(0x41615252)
#define	FSI_STRUCSIG	LE_32(0x61417272)
#define	FSI_TRAILSIG	LE_32(0xaa550000)	/* same as MBB_MAGIC */

#define	FSISIG_OK(fsi)	(						\
	((fat_od_fsi_t *)(fsi))->fsi_leadsig == FSI_LEADSIG &&		\
	((fat_od_fsi_t *)(fsi))->fsi_strucsig == FSI_STRUCSIG &&	\
	((fat_od_fsi_t *)(fsi))->fsi_trailsig == FSI_TRAILSIG)

#define	FSINFO_UNKNOWN	((uint32_t)(-1))	/* free/next not valid */

typedef enum { FAT12, FAT16, FAT32, FAT_UNKNOWN, FAT_QUESTIONABLE } fattype_t;


struct pcfs {
	struct vfs *pcfs_vfs;		/* vfs for this fs */
	int pcfs_flags;			/* flags */
	int pcfs_ldrive;		/* logical DOS drive number */
	fattype_t pcfs_fattype;
	dev_t pcfs_xdev;		/* actual device that is mounted */
	struct vnode *pcfs_devvp;	/*   and a vnode for it */
	int pcfs_secsize;		/* sector size in bytes */
	int pcfs_spcl;			/* sectors per cluster */
	int pcfs_spt;			/* sectors per track */
	int pcfs_sdshift;		/* shift to convert sector into */
					/* DEV_BSIZE "sectors"; assume */
					/* pcfs_secsize is 2**n times of */
					/* DEV_BSIZE */
	int pcfs_fatsec;		/* number of sec per FAT */
	int pcfs_numfat;		/* number of FAT copies */
	int pcfs_rdirsec;		/* number of sec in root dir */
	daddr_t pcfs_dosstart;		/* start blkno of DOS partition */
	daddr_t pcfs_fsistart;		/* start blkno of FSI sector */
	daddr_t pcfs_fatstart;		/* start blkno of first FAT */
	daddr_t pcfs_rdirstart;		/* start blkno of root dir */
	daddr_t pcfs_datastart;		/* start blkno of data area */
	int pcfs_clsize;		/* cluster size in bytes */
	int pcfs_ncluster;		/* number of clusters in fs */
	int pcfs_nrefs;			/* number of active pcnodes */
	int pcfs_frefs;			/* number of active file pcnodes */
	int pcfs_nxfrecls;		/* next free cluster */
	uchar_t *pcfs_fatp;		/* ptr to FAT data */
	uchar_t *pcfs_fat_changemap;	/* map of changed fat data */
	int pcfs_fat_changemapsize;	/* size of FAT changemap */
	time_t pcfs_fattime;		/* time FAT becomes invalid */
	time_t pcfs_verifytime;		/* time to reverify disk */
	kmutex_t	pcfs_lock;		/* per filesystem lock */
	kthread_id_t pcfs_owner;		/* id of thread locking pcfs */
	int pcfs_count;			/* # of pcfs locks for pcfs_owner */
	struct fat_fsinfo pcfs_fsinfo;	/* in-core fsinfo */
	struct pcfs *pcfs_nxt;		/* linked list of all mounts */
	int pcfs_fatjustread;		/* Used to flag a freshly found FAT */
	struct vnode *pcfs_root;	/* vnode for the root dir of the fs */
	int pcfs_secondswest;		/* recording timezone for this fs */
	len_t pcfs_mediasize;
	int pcfs_rootblksize;
	int pcfs_mediadesc;		/* media descriptor */
	pc_cluster32_t pcfs_lastclmark;
	pc_cluster32_t pcfs_rootclnum;
	timestruc_t pcfs_mounttime;	/* timestamp for "/" */
};

/*
 * flags
 */
#define	PCFS_FATMOD		0x01	/* FAT has been modified */
#define	PCFS_LOCKED		0x02	/* fs is locked */
#define	PCFS_WANTED		0x04	/* locked fs is wanted */
#define	PCFS_NOCHK		0x800	/* don't resync fat on error */
#define	PCFS_BOOTPART		0x1000	/* boot partition type */
#define	PCFS_HIDDEN		0x2000	/* show hidden files */
#define	PCFS_PCMCIA_NO_CIS	0x4000	/* PCMCIA psuedo floppy */
#define	PCFS_FOLDCASE		0x8000	/* fold filenames to lowercase */
#define	PCFS_FSINFO_OK		0x10000	/* valid FAT32 fsinfo sector */
#define	PCFS_IRRECOV		0x20000	/* FS was messed with during write */
#define	PCFS_NOCLAMPTIME	0x40000	/* expose full FAT timestamp range */
#define	PCFS_NOATIME		0x80000	/* disable atime updates */

#define	IS_FAT12(PCFS)	((PCFS)->pcfs_fattype == FAT12)
#define	IS_FAT16(PCFS)	((PCFS)->pcfs_fattype == FAT16)
#define	IS_FAT32(PCFS)	((PCFS)->pcfs_fattype == FAT32)

/* for compatibility */
struct old_pcfs_args {
	int	secondswest;	/* seconds west of Greenwich */
	int	dsttime;    	/* type of dst correction */
};

struct pcfs_args {
	int	secondswest;	/* seconds west of Greenwich */
	int	dsttime;    	/* type of dst correction */
	int	flags;
};

/*
 * pcfs mount options.
 */
#define	MNTOPT_PCFS_HIDDEN	"hidden"
#define	MNTOPT_PCFS_NOHIDDEN	"nohidden"
#define	MNTOPT_PCFS_FOLDCASE	"foldcase"
#define	MNTOPT_PCFS_NOFOLDCASE	"nofoldcase"
#define	MNTOPT_PCFS_CLAMPTIME	"clamptime"
#define	MNTOPT_PCFS_NOCLAMPTIME	"noclamptime"
#define	MNTOPT_PCFS_TIMEZONE	"timezone"
#define	MNTOPT_PCFS_SECSIZE	"secsize"

/*
 * Disk timeout value in sec.
 * This is used to time out the in core FAT and to re-verify the disk.
 * This should be less than the time it takes to change floppys
 */
#define	PCFS_DISKTIMEOUT	2

#define	PCFS_MAXOFFSET_T	UINT32_MAX	/* PCFS max file size */

#define	VFSTOPCFS(VFSP)		((struct pcfs *)((VFSP)->vfs_data))
#define	PCFSTOVFS(FSP)		((FSP)->pcfs_vfs)

/*
 * special cluster numbers in FAT
 */
#define	PCF_FREECLUSTER		0x00	/* cluster is available */
#define	PCF_ERRORCLUSTER	0x01	/* error occurred allocating cluster */
#define	PCF_12BCLUSTER		0xFF0	/* 12-bit version of reserved cluster */
#define	PCF_RESCLUSTER		0xFFF0	/* 16-bit version of reserved cluster */
#define	PCF_RESCLUSTER32	0xFFFFFF0 /* 32-bit version */
#define	PCF_BADCLUSTER		0xFFF7	/* bad cluster, do not use */
#define	PCF_BADCLUSTER32	0xFFFFFF7 /* 32-bit version */
#define	PCF_LASTCLUSTER		0xFFF8	/* >= means last cluster in file */
#define	PCF_LASTCLUSTER32	0xFFFFFF8 /* 32-bit version */
#define	PCF_LASTCLUSTERMARK	0xFFFF	/* value used to mark last cluster */
#define	PCF_LASTCLUSTERMARK32	0xFFFFFFF /* 32-bit version */
#define	PCF_FIRSTCLUSTER	2	/* first valid cluster number */

/*
 * file system constants
 */
#define	PC_MAXFATSEC	256		/* maximum number of sectors in FAT */

/*
 * file system parameter macros
 */

#define	pc_clear_fatchanges(PCFS) \
	bzero((PCFS)->pcfs_fat_changemap, (PCFS)->pcfs_fat_changemapsize)

#define	pc_blksize(PCFS, PCP, OFF)	/* file system block size */	\
	(((PCTOV(PCP)->v_flag & VROOT) && !IS_FAT32(PCFS)) ? \
	    ((OFF) >= \
	    ((PCFS)->pcfs_rdirsec & \
	    ~((PCFS)->pcfs_spcl - 1)) * ((PCFS)->pcfs_secsize)? \
	    ((PCFS)->pcfs_rdirsec & \
	    ((PCFS)->pcfs_spcl - 1)) * ((PCFS)->pcfs_secsize): \
	    (PCFS)->pcfs_clsize): \
	    (PCFS)->pcfs_clsize)

#define	pc_blkoff(PCFS, OFF)		/* offset within block */ \
	((int)((OFF) & ((PCFS)->pcfs_clsize - 1)))

#define	pc_lblkno(PCFS, OFF)		/* logical block (cluster) no */ \
	((daddr_t)((OFF) / (PCFS)->pcfs_clsize))

#define	pc_dbtocl(PCFS, DB)		/* disk blks to clusters */ \
	((int)((DB) / (PCFS)->pcfs_spcl))

#define	pc_cltodb(PCFS, CL)		/* clusters to disk blks */ \
	((daddr_t)((CL) * (PCFS)->pcfs_spcl))

#define	pc_dbdaddr(PCFS, DB)	/* sector to DEV_BSIZE "sector" addr */ \
	((DB) << (PCFS)->pcfs_sdshift)

#define	pc_daddrdb(PCFS, DADDR)	/* DEV_BSIZE "sector" addr to sector addr */ \
	((DADDR) >> (PCFS)->pcfs_sdshift)

#define	pc_cldaddr(PCFS, CL)	/* DEV_BSIZE "sector" addr for cluster */ \
	pc_dbdaddr(PCFS, ((daddr_t)((PCFS)->pcfs_datastart +	\
	pc_cltodb(PCFS, (CL) - PCF_FIRSTCLUSTER))))

#define	pc_daddrcl(PCFS, DADDR)		/* cluster for disk address */	\
	((int)(PCF_FIRSTCLUSTER +					\
	pc_dbtocl(pc_daddrdb(PCFS, DADDR) - (PCFS)->pcfs_datastart)))

/*
 * Number of directory entries per sector / cluster
 */
#define	pc_direntpersec(PCFS)						\
	((int)((PCFS)->pcfs_secsize / sizeof (struct pcdir)))

#define	pc_direntpercl(PCFS)						\
	((int)((PCFS)->pcfs_clsize  / sizeof (struct pcdir)))

/*
 * out-of-range check for cluster numbers.
 */
#define	pc_validcl(PCFS, CL)		/* check that cluster no is legit */ \
	((int)(CL) >= PCF_FIRSTCLUSTER && \
	    (int)(CL) < (PCFS)->pcfs_ncluster + PCF_FIRSTCLUSTER)

/*
 * external routines.
 */
extern int pc_lockfs(struct pcfs *, int, int); /* lock fs and get fat */
extern void pc_unlockfs(struct pcfs *);	/* ulock the fs */
extern int pc_getfat(struct pcfs *);	/* get fat from disk */
extern void pc_invalfat(struct pcfs *);	/* invalidate incore fat */
extern int pc_syncfat(struct pcfs *);	/* sync fat to disk */
extern int pc_freeclusters(struct pcfs *);	/* num free clusters in fs */
extern pc_cluster32_t pc_alloccluster(struct pcfs *, int);
extern void pc_setcluster(struct pcfs *, pc_cluster32_t, pc_cluster32_t);
extern void pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn);
extern int pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn);

/*
 * debugging
 */
extern int pcfsdebuglevel;
#define	PC_DPRINTF0(level, A) \
	if (pcfsdebuglevel >= level) \
	    cmn_err(CE_CONT, (A))
#define	PC_DPRINTF1(level, A, B) \
	if (pcfsdebuglevel >= level) \
	    cmn_err(CE_CONT, (A), (B))
#define	PC_DPRINTF2(level, A, B, C) \
	if (pcfsdebuglevel >= level) \
	    cmn_err(CE_CONT, (A), (B), (C))
#define	PC_DPRINTF3(level, A, B, C, D) \
	if (pcfsdebuglevel >= level) \
	    cmn_err(CE_CONT, (A), (B), (C), (D))
#define	PC_DPRINTF4(level, A, B, C, D, E) \
	if (pcfsdebuglevel >= level) \
	    cmn_err(CE_CONT, (A), (B), (C), (D), (E))

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_FS_PC_FS_H */