1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (C) 1995, 1997 Wolfgang Solfrank 5 * Copyright (c) 1995 Martin Husemann 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 29 #include <sys/cdefs.h> 30 #ifndef lint 31 __RCSID("$NetBSD: boot.c,v 1.22 2020/01/11 16:29:07 christos Exp $"); 32 #endif /* not lint */ 33 34 #include <sys/param.h> 35 36 #include <stdint.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <stdio.h> 40 #include <unistd.h> 41 42 #include "ext.h" 43 #include "fsutil.h" 44 45 int 46 readboot(int dosfs, struct bootblock *boot) 47 { 48 u_char block[DOSBOOTBLOCKSIZE]; 49 u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; 50 int ret = FSOK; 51 52 if ((size_t)read(dosfs, block, sizeof block) != sizeof block) { 53 perr("could not read boot block"); 54 return FSFATAL; 55 } 56 57 if (block[510] != 0x55 || block[511] != 0xaa) { 58 pfatal("Invalid signature in boot block: %02x%02x", 59 block[511], block[510]); 60 return FSFATAL; 61 } 62 63 memset(boot, 0, sizeof *boot); 64 boot->ValidFat = -1; 65 66 /* Decode BIOS Parameter Block */ 67 68 /* Bytes per sector: can only be 512, 1024, 2048 and 4096. */ 69 boot->bpbBytesPerSec = block[11] + (block[12] << 8); 70 if (boot->bpbBytesPerSec < DOSBOOTBLOCKSIZE_REAL || 71 boot->bpbBytesPerSec > DOSBOOTBLOCKSIZE || 72 !powerof2(boot->bpbBytesPerSec)) { 73 pfatal("Invalid sector size: %u", boot->bpbBytesPerSec); 74 return FSFATAL; 75 } 76 77 /* Sectors per cluster: can only be: 1, 2, 4, 8, 16, 32, 64, 128. */ 78 boot->bpbSecPerClust = block[13]; 79 if (boot->bpbSecPerClust == 0 || !powerof2(boot->bpbSecPerClust)) { 80 pfatal("Invalid cluster size: %u", boot->bpbSecPerClust); 81 return FSFATAL; 82 } 83 84 /* Reserved sectors: must be non-zero */ 85 boot->bpbResSectors = block[14] + (block[15] << 8); 86 if (boot->bpbResSectors < 1) { 87 pfatal("Invalid reserved sectors: %u", 88 boot->bpbResSectors); 89 return FSFATAL; 90 } 91 92 /* Number of FATs */ 93 boot->bpbFATs = block[16]; 94 if (boot->bpbFATs == 0) { 95 pfatal("Invalid number of FATs: %u", boot->bpbFATs); 96 return FSFATAL; 97 } 98 99 /* Root directory entries for FAT12 and FAT16 */ 100 boot->bpbRootDirEnts = block[17] + (block[18] << 8); 101 if (!boot->bpbRootDirEnts) { 102 /* bpbRootDirEnts = 0 suggests that we are FAT32 */ 103 boot->flags |= FAT32; 104 } 105 106 /* Total sectors (16 bits) */ 107 boot->bpbSectors = block[19] + (block[20] << 8); 108 if (boot->bpbSectors != 0 && (boot->flags & FAT32)) { 109 pfatal("Invalid 16-bit total sector count on FAT32: %u", 110 boot->bpbSectors); 111 return FSFATAL; 112 } 113 114 /* Media type: ignored */ 115 boot->bpbMedia = block[21]; 116 117 /* FAT12/FAT16: 16-bit count of sectors per FAT */ 118 boot->bpbFATsmall = block[22] + (block[23] << 8); 119 if (boot->bpbFATsmall != 0 && (boot->flags & FAT32)) { 120 pfatal("Invalid 16-bit FAT sector count on FAT32: %u", 121 boot->bpbFATsmall); 122 return FSFATAL; 123 } 124 125 /* Legacy CHS geometry numbers: ignored */ 126 boot->SecPerTrack = block[24] + (block[25] << 8); 127 boot->bpbHeads = block[26] + (block[27] << 8); 128 129 /* Hidden sectors: ignored */ 130 boot->bpbHiddenSecs = block[28] + (block[29] << 8) + 131 (block[30] << 16) + (block[31] << 24); 132 133 /* Total sectors (32 bits) */ 134 boot->bpbHugeSectors = block[32] + (block[33] << 8) + 135 (block[34] << 16) + (block[35] << 24); 136 if (boot->bpbHugeSectors == 0) { 137 if (boot->flags & FAT32) { 138 pfatal("FAT32 with sector count of zero"); 139 return FSFATAL; 140 } else if (boot->bpbSectors == 0) { 141 pfatal("FAT with sector count of zero"); 142 return FSFATAL; 143 } 144 boot->NumSectors = boot->bpbSectors; 145 } else { 146 if (boot->bpbSectors != 0) { 147 pfatal("Invalid FAT sector count"); 148 return FSFATAL; 149 } 150 boot->NumSectors = boot->bpbHugeSectors; 151 } 152 153 if (boot->flags & FAT32) { 154 /* If the OEM Name field is EXFAT, it's not FAT32, so bail */ 155 if (!memcmp(&block[3], "EXFAT ", 8)) { 156 pfatal("exFAT filesystem is not supported."); 157 return FSFATAL; 158 } 159 160 /* 32-bit count of sectors per FAT */ 161 boot->FATsecs = block[36] + (block[37] << 8) 162 + (block[38] << 16) + (block[39] << 24); 163 164 if (block[40] & 0x80) 165 boot->ValidFat = block[40] & 0x0f; 166 167 /* FAT32 version, bail out if not 0.0 */ 168 if (block[42] || block[43]) { 169 pfatal("Unknown file system version: %x.%x", 170 block[43], block[42]); 171 return FSFATAL; 172 } 173 174 /* 175 * Cluster number of the first cluster of root directory. 176 * 177 * Should be 2 but do not require it. 178 */ 179 boot->bpbRootClust = block[44] + (block[45] << 8) 180 + (block[46] << 16) + (block[47] << 24); 181 182 /* Sector number of the FSInfo structure, usually 1 */ 183 boot->bpbFSInfo = block[48] + (block[49] << 8); 184 185 /* Sector number of the backup boot block, ignored */ 186 boot->bpbBackup = block[50] + (block[51] << 8); 187 188 /* Check basic parameters */ 189 if (boot->bpbFSInfo == 0) { 190 /* 191 * Either the BIOS Parameter Block has been corrupted, 192 * or this is not a FAT32 filesystem, most likely an 193 * exFAT filesystem. 194 */ 195 pfatal("Invalid FAT32 Extended BIOS Parameter Block"); 196 return FSFATAL; 197 } 198 199 /* Read in and verify the FSInfo block */ 200 if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, 201 SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec 202 || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { 203 perr("could not read fsinfo block"); 204 return FSFATAL; 205 } 206 if (memcmp(fsinfo, "RRaA", 4) 207 || memcmp(fsinfo + 0x1e4, "rrAa", 4) 208 || fsinfo[0x1fc] 209 || fsinfo[0x1fd] 210 || fsinfo[0x1fe] != 0x55 211 || fsinfo[0x1ff] != 0xaa 212 || fsinfo[0x3fc] 213 || fsinfo[0x3fd] 214 || fsinfo[0x3fe] != 0x55 215 || fsinfo[0x3ff] != 0xaa) { 216 pwarn("Invalid signature in fsinfo block\n"); 217 if (ask(0, "Fix")) { 218 memcpy(fsinfo, "RRaA", 4); 219 memcpy(fsinfo + 0x1e4, "rrAa", 4); 220 fsinfo[0x1fc] = fsinfo[0x1fd] = 0; 221 fsinfo[0x1fe] = 0x55; 222 fsinfo[0x1ff] = 0xaa; 223 fsinfo[0x3fc] = fsinfo[0x3fd] = 0; 224 fsinfo[0x3fe] = 0x55; 225 fsinfo[0x3ff] = 0xaa; 226 if (lseek(dosfs, boot->bpbFSInfo * 227 boot->bpbBytesPerSec, SEEK_SET) 228 != boot->bpbFSInfo * boot->bpbBytesPerSec 229 || write(dosfs, fsinfo, sizeof fsinfo) 230 != sizeof fsinfo) { 231 perr("Unable to write bpbFSInfo"); 232 return FSFATAL; 233 } 234 ret = FSBOOTMOD; 235 } else 236 boot->bpbFSInfo = 0; 237 } else { 238 /* We appear to have a valid FSInfo block, decode */ 239 boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) 240 + (fsinfo[0x1ea] << 16) 241 + (fsinfo[0x1eb] << 24); 242 boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) 243 + (fsinfo[0x1ee] << 16) 244 + (fsinfo[0x1ef] << 24); 245 } 246 } else { 247 /* !FAT32: FAT12/FAT16 */ 248 boot->FATsecs = boot->bpbFATsmall; 249 } 250 251 if (boot->FATsecs < 1 || boot->FATsecs > UINT32_MAX / boot->bpbFATs) { 252 pfatal("Invalid FATs(%u) with FATsecs(%zu)", 253 boot->bpbFATs, (size_t)boot->FATsecs); 254 return FSFATAL; 255 } 256 257 boot->FirstCluster = (boot->bpbRootDirEnts * 32 + 258 boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec + 259 boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; 260 261 if (boot->FirstCluster + boot->bpbSecPerClust > boot->NumSectors) { 262 pfatal("Cluster offset too large (%u clusters)\n", 263 boot->FirstCluster); 264 return FSFATAL; 265 } 266 267 /* 268 * The number of clusters is derived from available data sectors, 269 * divided by sectors per cluster. 270 */ 271 boot->NumClusters = 272 (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust; 273 274 if (boot->flags & FAT32) { 275 if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) { 276 pfatal("Filesystem too big (%u clusters) for FAT32 partition", 277 boot->NumClusters); 278 return FSFATAL; 279 } 280 if (boot->NumClusters < (CLUST_RSRVD & CLUST16_MASK)) { 281 pfatal("Filesystem too small (%u clusters) for FAT32 partition", 282 boot->NumClusters); 283 return FSFATAL; 284 } 285 boot->ClustMask = CLUST32_MASK; 286 287 if (boot->bpbRootClust < CLUST_FIRST || 288 boot->bpbRootClust >= boot->NumClusters) { 289 pfatal("Root directory starts with cluster out of range(%u)", 290 boot->bpbRootClust); 291 return FSFATAL; 292 } 293 } else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) { 294 boot->ClustMask = CLUST12_MASK; 295 } else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) { 296 boot->ClustMask = CLUST16_MASK; 297 } else { 298 pfatal("Filesystem too big (%u clusters) for non-FAT32 partition", 299 boot->NumClusters); 300 return FSFATAL; 301 } 302 303 switch (boot->ClustMask) { 304 case CLUST32_MASK: 305 boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4; 306 break; 307 case CLUST16_MASK: 308 boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2; 309 break; 310 default: 311 boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3; 312 break; 313 } 314 315 if (boot->NumFatEntries < boot->NumClusters) { 316 pfatal("FAT size too small, %u entries won't fit into %u sectors\n", 317 boot->NumClusters, boot->FATsecs); 318 return FSFATAL; 319 } 320 321 /* 322 * There are two reserved clusters. To avoid adding CLUST_FIRST every 323 * time we perform boundary checks, we increment the NumClusters by 2, 324 * which is CLUST_FIRST to denote the first out-of-range cluster number. 325 */ 326 boot->NumClusters += CLUST_FIRST; 327 328 boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust; 329 330 boot->NumFiles = 1; 331 boot->NumFree = 0; 332 333 return ret; 334 } 335 336 int 337 writefsinfo(int dosfs, struct bootblock *boot) 338 { 339 u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; 340 341 if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) 342 != boot->bpbFSInfo * boot->bpbBytesPerSec 343 || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { 344 perr("could not read fsinfo block"); 345 return FSFATAL; 346 } 347 fsinfo[0x1e8] = (u_char)boot->FSFree; 348 fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); 349 fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); 350 fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); 351 fsinfo[0x1ec] = (u_char)boot->FSNext; 352 fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); 353 fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); 354 fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); 355 if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) 356 != boot->bpbFSInfo * boot->bpbBytesPerSec 357 || write(dosfs, fsinfo, sizeof fsinfo) 358 != sizeof fsinfo) { 359 perr("Unable to write bpbFSInfo"); 360 return FSFATAL; 361 } 362 /* 363 * Technically, we should return FSBOOTMOD here. 364 * 365 * However, since Win95 OSR2 (the first M$ OS that has 366 * support for FAT32) doesn't maintain the FSINFO block 367 * correctly, it has to be fixed pretty often. 368 * 369 * Therefore, we handle the FSINFO block only informally, 370 * fixing it if necessary, but otherwise ignoring the 371 * fact that it was incorrect. 372 */ 373 return 0; 374 } 375