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 these two macros are obsolete - do not use them. 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 #define MNTOPT_PCFS_HIDDEN "hidden" 234 #define MNTOPT_PCFS_NOHIDDEN "nohidden" 235 #define MNTOPT_PCFS_FOLDCASE "foldcase" 236 #define MNTOPT_PCFS_NOFOLDCASE "nofoldcase" 237 238 /* 239 * Disk timeout value in sec. 240 * This is used to time out the in core FAT and to re-verify the disk. 241 * This should be less than the time it takes to change floppys 242 */ 243 #define PCFS_DISKTIMEOUT 2 244 245 #define VFSTOPCFS(VFSP) ((struct pcfs *)((VFSP)->vfs_data)) 246 #define PCFSTOVFS(FSP) ((FSP)->pcfs_vfs) 247 248 /* 249 * special cluster numbers in FAT 250 */ 251 #define PCF_FREECLUSTER 0x00 /* cluster is available */ 252 #define PCF_ERRORCLUSTER 0x01 /* error occurred allocating cluster */ 253 #define PCF_12BCLUSTER 0xFF0 /* 12-bit version of reserved cluster */ 254 #define PCF_RESCLUSTER 0xFFF0 /* 16-bit version of reserved cluster */ 255 #define PCF_RESCLUSTER32 0xFFFFFF0 /* 32-bit version */ 256 #define PCF_BADCLUSTER 0xFFF7 /* bad cluster, do not use */ 257 #define PCF_BADCLUSTER32 0xFFFFFF7 /* 32-bit version */ 258 #define PCF_LASTCLUSTER 0xFFF8 /* >= means last cluster in file */ 259 #define PCF_LASTCLUSTER32 0xFFFFFF8 /* 32-bit version */ 260 #define PCF_LASTCLUSTERMARK 0xFFFF /* value used to mark last cluster */ 261 #define PCF_LASTCLUSTERMARK32 0xFFFFFFF /* 32-bit version */ 262 #define PCF_FIRSTCLUSTER 2 /* first valid cluster number */ 263 264 /* 265 * file system constants 266 */ 267 #define PC_MAXFATSEC 256 /* maximum number of sectors in FAT */ 268 269 /* 270 * file system parameter macros 271 */ 272 273 #define IS_FAT32(PCFS) \ 274 (((PCFS)->pcfs_flags & PCFS_FAT32) == PCFS_FAT32) 275 276 #define IS_FAT16(PCFS) \ 277 (((PCFS)->pcfs_flags & PCFS_FAT16) == PCFS_FAT16) 278 279 #define IS_FAT12(PCFS) \ 280 (((PCFS)->pcfs_flags & (PCFS_FAT16 | PCFS_FAT32)) == 0) 281 282 #define pc_clear_fatchanges(PCFS) \ 283 bzero((PCFS)->pcfs_fat_changemap, (PCFS)->pcfs_fat_changemapsize) 284 285 #define pc_blksize(PCFS, PCP, OFF) /* file system block size */ \ 286 (((PCTOV(PCP)->v_flag & VROOT) && !IS_FAT32(PCFS)) ? \ 287 ((OFF) >= \ 288 ((PCFS)->pcfs_rdirsec & \ 289 ~((PCFS)->pcfs_spcl - 1)) * ((PCFS)->pcfs_secsize)? \ 290 ((PCFS)->pcfs_rdirsec & \ 291 ((PCFS)->pcfs_spcl - 1)) * ((PCFS)->pcfs_secsize): \ 292 (PCFS)->pcfs_clsize): \ 293 (PCFS)->pcfs_clsize) 294 295 #define pc_blkoff(PCFS, OFF) /* offset within block */ \ 296 ((int)((OFF) & ((PCFS)->pcfs_clsize - 1))) 297 298 #define pc_lblkno(PCFS, OFF) /* logical block (cluster) no */ \ 299 ((daddr_t)((OFF) / (PCFS)->pcfs_clsize)) 300 301 #define pc_dbtocl(PCFS, DB) /* disk blks to clusters */ \ 302 ((int)((DB) / (PCFS)->pcfs_spcl)) 303 304 #define pc_cltodb(PCFS, CL) /* clusters to disk blks */ \ 305 ((daddr_t)((CL) * (PCFS)->pcfs_spcl)) 306 307 #define pc_cldaddr(PCFS, CL) /* DEV_BSIZE "sector" addr for cluster */ \ 308 (((daddr_t)((PCFS)->pcfs_datastart + \ 309 ((CL) - PCF_FIRSTCLUSTER) * (PCFS)->pcfs_spcl)) << \ 310 (PCFS)->pcfs_sdshift) 311 312 #define pc_daddrcl(PCFS, DADDR) /* cluster for disk address */ \ 313 ((int)(((((DADDR) >> (PCFS)->pcfs_sdshift) - (PCFS)->pcfs_datastart) / \ 314 (PCFS)->pcfs_spcl) + 2)) 315 316 #define pc_dbdaddr(PCFS, DB) /* sector to DEV_BSIZE "sector" addr */ \ 317 ((DB) << (PCFS)->pcfs_sdshift) 318 319 #define pc_daddrdb(PCFS, DADDR) /* DEV_BSIZE "sector" addr to sector addr */ \ 320 ((DADDR) >> (PCFS)->pcfs_sdshift) 321 322 #define pc_validcl(PCFS, CL) /* check that cluster no is legit */ \ 323 ((int)(CL) >= PCF_FIRSTCLUSTER && \ 324 (int)(CL) <= (PCFS)->pcfs_ncluster) 325 326 /* 327 * external routines. 328 */ 329 extern int pc_lockfs(struct pcfs *, int, int); /* lock fs and get fat */ 330 extern void pc_unlockfs(struct pcfs *); /* ulock the fs */ 331 extern int pc_getfat(struct pcfs *); /* get fat from disk */ 332 extern void pc_invalfat(struct pcfs *); /* invalidate incore fat */ 333 extern int pc_syncfat(struct pcfs *); /* sync fat to disk */ 334 extern int pc_freeclusters(struct pcfs *); /* num free clusters in fs */ 335 extern pc_cluster32_t pc_alloccluster(struct pcfs *, int); 336 extern void pc_setcluster(struct pcfs *, pc_cluster32_t, pc_cluster32_t); 337 extern void pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn); 338 extern int pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn); 339 340 /* 341 * debugging 342 */ 343 extern int pcfsdebuglevel; 344 #define PC_DPRINTF0(level, A) \ 345 if (pcfsdebuglevel >= level) \ 346 cmn_err(CE_CONT, (A)) 347 #define PC_DPRINTF1(level, A, B) \ 348 if (pcfsdebuglevel >= level) \ 349 cmn_err(CE_CONT, (A), (B)) 350 #define PC_DPRINTF2(level, A, B, C) \ 351 if (pcfsdebuglevel >= level) \ 352 cmn_err(CE_CONT, (A), (B), (C)) 353 #define PC_DPRINTF3(level, A, B, C, D) \ 354 if (pcfsdebuglevel >= level) \ 355 cmn_err(CE_CONT, (A), (B), (C), (D)) 356 #define PC_DPRINTF4(level, A, B, C, D, E) \ 357 if (pcfsdebuglevel >= level) \ 358 cmn_err(CE_CONT, (A), (B), (C), (D), (E)) 359 360 #ifdef __cplusplus 361 } 362 #endif 363 364 #endif /* _SYS_FS_PC_FS_H */ 365