xref: /illumos-gate/usr/src/uts/common/sys/fs/pc_fs.h (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #ifndef	_SYS_FS_PC_FS_H
28 #define	_SYS_FS_PC_FS_H
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/thread.h>
33 
34 #ifdef	__cplusplus
35 extern "C" {
36 #endif
37 
38 typedef	uint16_t	pc_cluster16_t;
39 typedef	uint32_t	pc_cluster32_t;
40 
41 /*
42  * PC (MSDOS) compatible virtual file system.
43  *
44  * A main goal of the implementation was to maintain statelessness
45  * except while files are open. Thus mounting and unmounting merely
46  * declared the file system name. The user may change disks at almost
47  * any time without concern (just like the PC). It is assumed that when
48  * files are open for writing the disk access light will be on, as a
49  * warning not to change disks. The implementation must, however, detect
50  * disk change and recover gracefully. It does this by comparing the
51  * in core entry for a directory to the on disk entry whenever a directory
52  * is searched. If a discrepancy is found active directories become root and
53  * active files are marked invalid.
54  *
55  * There are only two type of nodes on the PC file system; files and
56  * directories. These are represented by two separate vnode op vectors,
57  * and they are kept in two separate tables. Files are known by the
58  * disk block number and block (cluster) offset of the files directory
59  * entry. Directories are known by the starting cluster number.
60  *
61  * The file system is locked for during each user operation. This is
62  * done to simplify disk verification error conditions.
63  *
64  * Notes on FAT32 support
65  * ----------------------
66  * The basic difference between FAT32 and FAT16 is that cluster numbers are now
67  * 32-bit instead of 16-bit. The FAT is thus an array of 32-bit cluster numbers,
68  * and because of this the cluster size can be much smaller on a large disk
69  * (4k, say, on a 1 Gig drive instead of 16k). Unfortunately, the FAT is not
70  * the only place cluster numbers are stored - the starting cluster is stored
71  * in the directory entry for a file, and of course it's only 16-bit. Luckily,
72  * there's a 16-bit OS/2 Extended Attribute field that is now used to store the
73  * upper 16-bits of the starting cluster number.
74  *
75  * Most of the FAT32 changes to pcfs are under 'if it's FAT32' to minimize the
76  * effect on non-FAT32 filesystems (and still share the code), except for the
77  * starting cluster changes. It seemed easier to make common functions to
78  * handle that.
79  *
80  * Other changes:
81  *
82  *     1. FAT32 partitions are indicated by partition types 0xB and 0xC.
83  *     2. The boot sector is now 2 sectors, to make room for FAT32 extensions.
84  *     3. The root directory is no longer stored in a fixed location. Its'
85  *        starting cluster is stored in the extended boot sector.
86  *     4. "Summary information" is now stored and we need to (at least) maintain
87  *        the number of free clusters or scandisk will be upset. Though the
88  *        sector this info is in is pointed to by the extensions in the boot
89  *        sector, the magic offset of this information is just that so
90  *        far - magic. 0x1e0.
91  *     5. FAT32 can use the alternate FAT. But we don't.
92  *
93  * FAT32 also exposed a latent bug: we bread() each copy of the FAT in one
94  * big chunk.  This is not good on a large FAT32 drive, such as a 1 Gig
95  * Jaz drive that has 4k clusters, since the FAT becomes 1 Meg in size and
96  * bread blocks forever. So now we read the FAT in chunks.
97  */
98 
99 /*
100  * pre-FAT32 boot sector.
101  */
102 struct bootsec {
103 	uchar_t	instr[3];
104 	uchar_t	version[8];
105 	uchar_t	bps[2];			/* bytes per sector */
106 	uchar_t	spcl;			/* sectors per allocation unit */
107 	uchar_t	res_sec[2];		/* reserved sectors, starting at 0 */
108 	uchar_t	nfat;			/* number of FATs */
109 	uchar_t	rdirents[2];		/* number of root directory entries */
110 	uchar_t	numsect[2];		/* old total sectors in logical image */
111 	uchar_t	mediadesriptor;		/* media descriptor byte */
112 	ushort_t fatsec;			/* number of sectors per FAT */
113 	ushort_t spt;			/* sectors per track */
114 	ushort_t nhead;			/* number of heads */
115 	ushort_t hiddensec;		/* number of hidden sectors */
116 	uint_t	totalsec;		/* total sectors in logical image */
117 };
118 
119 /*
120  * FAT32 volumes have a bigger boot sector. They include the normal
121  * boot sector.
122  */
123 struct fat32_bootsec {
124 	struct bootsec	f_bs;
125 	uint32_t	f_fatlength;	/* size of FAT */
126 	uint16_t	f_flags;
127 	uint8_t		f_major;	/* major filesystem version #? */
128 	uint8_t		f_minor;	/* minor filesystem version #? */
129 	uint32_t	f_rootcluster;	/* first cluster in root directory */
130 	uint16_t	f_infosector;	/* where summary info is */
131 	uint16_t	f_backupboot;	/* backup boot sector */
132 	uint16_t	f_reserved2[6];
133 };
134 
135 #define	FAT32_FS_SIGN	0x61417272
136 #define	FAT32_BOOT_FSINFO_OFF	0x1e0
137 
138 /*
139  * summary information for fat32 volumes. We need to maintain fs_free_clusters
140  * or Microsoft Scandisk will be upset.
141  */
142 struct fat32_boot_fsinfo {
143 	uint32_t	fs_reserved1;
144 	uint32_t	fs_signature;	/* 0x61417272 */
145 	uint32_t	fs_free_clusters;  /* # free clusters. -1 if unknown */
146 	uint32_t	fs_next_cluster;   /* unused by pcfs */
147 	uint32_t	fs_reserved2[4];
148 };
149 
150 #define	FSINFO_UNKNOWN	(-1)
151 
152 struct pcfs {
153 	struct vfs *pcfs_vfs;		/* vfs for this fs */
154 	int pcfs_flags;			/* flags */
155 	int pcfs_ldrv;			/* logical DOS drive number */
156 	dev_t pcfs_xdev;		/* actual device that is mounted */
157 	struct vnode *pcfs_devvp;	/*   and a vnode for it */
158 	int pcfs_secsize;		/* sector size in bytes */
159 	int pcfs_spcl;			/* sectors per cluster */
160 	int pcfs_spt;			/* sectors per track */
161 	int pcfs_sdshift;		/* shift to convert sector into */
162 					/* DEV_BSIZE "sectors"; assume */
163 					/* pcfs_secsize is 2**n times of */
164 					/* DEV_BSIZE */
165 	int pcfs_fatsec;		/* number of sec per FAT */
166 	int pcfs_numfat;		/* number of FAT copies */
167 	int pcfs_rdirsec;		/* number of sec in root dir */
168 	daddr_t pcfs_dosstart;		/* start blkno of DOS partition */
169 	daddr_t pcfs_fatstart;		/* start blkno of first FAT */
170 	daddr_t pcfs_rdirstart;		/* start blkno of root dir */
171 	daddr_t pcfs_datastart;		/* start blkno of data area */
172 	int pcfs_clsize;		/* cluster size in bytes */
173 	int pcfs_ncluster;		/* number of clusters in fs */
174 	int pcfs_entps;			/* number of dir entry per sector */
175 	int pcfs_nrefs;			/* number of active pcnodes */
176 	int pcfs_frefs;			/* number of active file pcnodes */
177 	int pcfs_nxfrecls;		/* next free cluster */
178 	uchar_t *pcfs_fatp;		/* ptr to FAT data */
179 	uchar_t *pcfs_fat_changemap;	/* map of changed fat data */
180 	int pcfs_fatsize;		/* size of FAT data */
181 	int pcfs_fat_changemapsize;	/* size of FAT changemap */
182 	time_t pcfs_fattime;		/* time FAT becomes invalid */
183 	time_t pcfs_verifytime;		/* time to reverify disk */
184 	kmutex_t	pcfs_lock;		/* per filesystem lock */
185 	kthread_id_t pcfs_owner;		/* id of thread locking pcfs */
186 	int pcfs_count;			/* # of pcfs locks for pcfs_owner */
187 	struct fat32_boot_fsinfo fsinfo_native; /* native fsinfo for fat32 */
188 	uint32_t	f32fsinfo_sector; /* where to read/write fsinfo */
189 	struct pcfs *pcfs_nxt;		/* linked list of all mounts */
190 	int pcfs_fatjustread;		/* Used to flag a freshly found FAT */
191 };
192 
193 /*
194  * flags
195  */
196 #define	PCFS_FATMOD	0x01		/* FAT has been modified */
197 #define	PCFS_LOCKED	0x02		/* fs is locked */
198 #define	PCFS_WANTED	0x04		/* locked fs is wanted */
199 #define	PCFS_FAT16	0x400		/* 16 bit FAT */
200 #define	PCFS_NOCHK	0x800		/* don't resync fat on error */
201 #define	PCFS_BOOTPART	0x1000		/* boot partition type */
202 #define	PCFS_HIDDEN	0x2000		/* show hidden files */
203 #define	PCFS_PCMCIA_NO_CIS 0x4000	/* PCMCIA psuedo floppy */
204 #define	PCFS_FOLDCASE	0x8000		/* fold all names from media to */
205 					/* lowercase */
206 #define	PCFS_FAT32	0x10000		/* 32 bit FAT */
207 #define	PCFS_IRRECOV	0x20000		/* FS was messed with during write */
208 
209 /* for compatibility */
210 struct old_pcfs_args {
211 	int	secondswest;	/* seconds west of Greenwich */
212 	int	dsttime;    	/* type of dst correction */
213 };
214 
215 struct pcfs_args {
216 	int	secondswest;	/* seconds west of Greenwich */
217 	int	dsttime;    	/* type of dst correction */
218 	int	flags;
219 };
220 
221 /*
222  * flags for the pcfs_args 'flags' field.
223  *
224  * Note that PCFS_MNT_HIDDEN is private - you should not be using it.
225  */
226 #define	PCFS_MNT_HIDDEN		0x01	/* show hidden files */
227 #define	PCFS_MNT_FOLDCASE	0x02	/* fold all names from media to */
228 					/* lowercase */
229 
230 /*
231  * pcfs mount options.
232  *
233  * Note that hidden/nohidden is private - you should not use it.
234  */
235 #define	MNTOPT_PCFS_HIDDEN	"hidden"
236 #define	MNTOPT_PCFS_NOHIDDEN	"nohidden"
237 #define	MNTOPT_PCFS_FOLDCASE	"foldcase"
238 #define	MNTOPT_PCFS_NOFOLDCASE	"nofoldcase"
239 
240 /*
241  * Disk timeout value in sec.
242  * This is used to time out the in core FAT and to re-verify the disk.
243  * This should be less than the time it takes to change floppys
244  */
245 #define	PCFS_DISKTIMEOUT	2
246 
247 #define	VFSTOPCFS(VFSP)		((struct pcfs *)((VFSP)->vfs_data))
248 #define	PCFSTOVFS(FSP)		((FSP)->pcfs_vfs)
249 
250 /*
251  * special cluster numbers in FAT
252  */
253 #define	PCF_FREECLUSTER		0x00	/* cluster is available */
254 #define	PCF_ERRORCLUSTER	0x01	/* error occurred allocating cluster */
255 #define	PCF_12BCLUSTER		0xFF0	/* 12-bit version of reserved cluster */
256 #define	PCF_RESCLUSTER		0xFFF0	/* 16-bit version of reserved cluster */
257 #define	PCF_RESCLUSTER32	0xFFFFFF0 /* 32-bit version */
258 #define	PCF_BADCLUSTER		0xFFF7	/* bad cluster, do not use */
259 #define	PCF_BADCLUSTER32	0xFFFFFF7 /* 32-bit version */
260 #define	PCF_LASTCLUSTER		0xFFF8	/* >= means last cluster in file */
261 #define	PCF_LASTCLUSTER32	0xFFFFFF8 /* 32-bit version */
262 #define	PCF_LASTCLUSTERMARK	0xFFFF	/* value used to mark last cluster */
263 #define	PCF_LASTCLUSTERMARK32	0xFFFFFFF /* 32-bit version */
264 #define	PCF_FIRSTCLUSTER	2	/* first valid cluster number */
265 
266 /*
267  * file system constants
268  */
269 #define	PC_MAXFATSEC	256		/* maximum number of sectors in FAT */
270 
271 /*
272  * file system parameter macros
273  */
274 
275 #define	IS_FAT32(PCFS) \
276 	(((PCFS)->pcfs_flags & PCFS_FAT32) == PCFS_FAT32)
277 
278 #define	IS_FAT16(PCFS) \
279 	(((PCFS)->pcfs_flags & PCFS_FAT16) == PCFS_FAT16)
280 
281 #define	IS_FAT12(PCFS) \
282 	(((PCFS)->pcfs_flags & (PCFS_FAT16 | PCFS_FAT32)) == 0)
283 
284 #define	pc_clear_fatchanges(PCFS) \
285 	bzero((PCFS)->pcfs_fat_changemap, (PCFS)->pcfs_fat_changemapsize)
286 
287 #define	pc_blksize(PCFS, PCP, OFF)	/* file system block size */ \
288 	(((PCTOV(PCP)->v_flag & VROOT) && !IS_FAT32(PCFS)) ? \
289 	    ((OFF) >= \
290 	    ((PCFS)->pcfs_rdirsec & \
291 	    ~((PCFS)->pcfs_spcl - 1)) * ((PCFS)->pcfs_secsize)? \
292 	    ((PCFS)->pcfs_rdirsec & \
293 	    ((PCFS)->pcfs_spcl - 1)) * ((PCFS)->pcfs_secsize): \
294 	    (PCFS)->pcfs_clsize): \
295 	    (PCFS)->pcfs_clsize)
296 
297 #define	pc_blkoff(PCFS, OFF)		/* offset within block */ \
298 	((int)((OFF) & ((PCFS)->pcfs_clsize - 1)))
299 
300 #define	pc_lblkno(PCFS, OFF)		/* logical block (cluster) no */ \
301 	((daddr_t)((OFF) / (PCFS)->pcfs_clsize))
302 
303 #define	pc_dbtocl(PCFS, DB)		/* disk blks to clusters */ \
304 	((int)((DB) / (PCFS)->pcfs_spcl))
305 
306 #define	pc_cltodb(PCFS, CL)		/* clusters to disk blks */ \
307 	((daddr_t)((CL) * (PCFS)->pcfs_spcl))
308 
309 #define	pc_cldaddr(PCFS, CL)	/* DEV_BSIZE "sector" addr for cluster */ \
310 	(((daddr_t)((PCFS)->pcfs_datastart + \
311 	    ((CL) - PCF_FIRSTCLUSTER) * (PCFS)->pcfs_spcl)) << \
312 	    (PCFS)->pcfs_sdshift)
313 
314 #define	pc_daddrcl(PCFS, DADDR)		/* cluster for disk address */ \
315 	((int)(((((DADDR) >> (PCFS)->pcfs_sdshift) - (PCFS)->pcfs_datastart) / \
316 	(PCFS)->pcfs_spcl) + 2))
317 
318 #define	pc_dbdaddr(PCFS, DB)	/* sector to DEV_BSIZE "sector" addr */ \
319 	((DB) << (PCFS)->pcfs_sdshift)
320 
321 #define	pc_daddrdb(PCFS, DADDR)	/* DEV_BSIZE "sector" addr to sector addr */ \
322 	((DADDR) >> (PCFS)->pcfs_sdshift)
323 
324 #define	pc_validcl(PCFS, CL)		/* check that cluster no is legit */ \
325 	((int)(CL) >= PCF_FIRSTCLUSTER && \
326 	    (int)(CL) <= (PCFS)->pcfs_ncluster)
327 
328 /*
329  * external routines.
330  */
331 extern int pc_lockfs(struct pcfs *, int, int); /* lock fs and get fat */
332 extern void pc_unlockfs(struct pcfs *);	/* ulock the fs */
333 extern int pc_getfat(struct pcfs *);	/* get fat from disk */
334 extern void pc_invalfat(struct pcfs *);	/* invalidate incore fat */
335 extern int pc_syncfat(struct pcfs *);	/* sync fat to disk */
336 extern int pc_freeclusters(struct pcfs *);	/* num free clusters in fs */
337 extern pc_cluster32_t pc_alloccluster(struct pcfs *, int);
338 extern void pc_setcluster(struct pcfs *, pc_cluster32_t, pc_cluster32_t);
339 extern void pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn);
340 extern int pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn);
341 
342 /*
343  * debugging
344  */
345 extern int pcfsdebuglevel;
346 #define	PC_DPRINTF0(level, A) \
347 	if (pcfsdebuglevel >= level) \
348 	    cmn_err(CE_CONT, (A))
349 #define	PC_DPRINTF1(level, A, B) \
350 	if (pcfsdebuglevel >= level) \
351 	    cmn_err(CE_CONT, (A), (B))
352 #define	PC_DPRINTF2(level, A, B, C) \
353 	if (pcfsdebuglevel >= level) \
354 	    cmn_err(CE_CONT, (A), (B), (C))
355 #define	PC_DPRINTF3(level, A, B, C, D) \
356 	if (pcfsdebuglevel >= level) \
357 	    cmn_err(CE_CONT, (A), (B), (C), (D))
358 #define	PC_DPRINTF4(level, A, B, C, D, E) \
359 	if (pcfsdebuglevel >= level) \
360 	    cmn_err(CE_CONT, (A), (B), (C), (D), (E))
361 
362 #ifdef	__cplusplus
363 }
364 #endif
365 
366 #endif	/* _SYS_FS_PC_FS_H */
367