1/* 2 * Copyright (C) 1995, 1997 Wolfgang Solfrank 3 * Copyright (c) 1995 Martin Husemann 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 27#include <stdlib.h> 28#include <string.h> 29#include <stdio.h> 30#include <unistd.h> 31 32#include "ext.h" 33 34int 35readboot(int dosfs, struct bootblock *boot) 36{ 37 u_char block[DOSBOOTBLOCKSIZE]; 38 u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; 39 u_char backup[DOSBOOTBLOCKSIZE]; 40 int ret = FSOK; 41 int i; 42 43 if ((size_t)read(dosfs, block, sizeof block) != sizeof block) { 44 perr("could not read boot block"); 45 return FSFATAL; 46 } 47 48 if (block[510] != 0x55 || block[511] != 0xaa) { 49 pfatal("Invalid signature in boot block: %02x%02x", 50 block[511], block[510]); 51 return FSFATAL; 52 } 53 54 memset(boot, 0, sizeof *boot); 55 boot->ValidFat = -1; 56 57 /* decode bios parameter block */ 58 boot->bpbBytesPerSec = block[11] + (block[12] << 8); 59 boot->bpbSecPerClust = block[13]; 60 boot->bpbResSectors = block[14] + (block[15] << 8); 61 boot->bpbFATs = block[16]; 62 boot->bpbRootDirEnts = block[17] + (block[18] << 8); 63 boot->bpbSectors = block[19] + (block[20] << 8); 64 boot->bpbMedia = block[21]; 65 boot->bpbFATsmall = block[22] + (block[23] << 8); 66 boot->SecPerTrack = block[24] + (block[25] << 8); 67 boot->bpbHeads = block[26] + (block[27] << 8); 68 boot->bpbHiddenSecs = block[28] + (block[29] << 8) + 69 (block[30] << 16) + (block[31] << 24); 70 boot->bpbHugeSectors = block[32] + (block[33] << 8) + 71 (block[34] << 16) + (block[35] << 24); 72 73 boot->FATsecs = boot->bpbFATsmall; 74 75 if (!boot->bpbRootDirEnts) 76 boot->flags |= FAT32; 77 if (boot->flags & FAT32) { 78 boot->FATsecs = block[36] + (block[37] << 8) 79 + (block[38] << 16) + (block[39] << 24); 80 if (block[40] & 0x80) 81 boot->ValidFat = block[40] & 0x0f; 82 83 /* check version number: */ 84 if (block[42] || block[43]) { 85 /* Correct? XXX */ 86 pfatal("Unknown file system version: %x.%x", 87 block[43], block[42]); 88 return FSFATAL; 89 } 90 boot->bpbRootClust = block[44] + (block[45] << 8) 91 + (block[46] << 16) + (block[47] << 24); 92 boot->bpbFSInfo = block[48] + (block[49] << 8); 93 boot->bpbBackup = block[50] + (block[51] << 8); 94 95 if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, 96 SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec 97 || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { 98 perr("could not read fsinfo block"); 99 return FSFATAL; 100 } 101 if (memcmp(fsinfo, "RRaA", 4) 102 || memcmp(fsinfo + 0x1e4, "rrAa", 4) 103 || fsinfo[0x1fc] 104 || fsinfo[0x1fd] 105 || fsinfo[0x1fe] != 0x55 106 || fsinfo[0x1ff] != 0xaa 107 || fsinfo[0x3fc] 108 || fsinfo[0x3fd] 109 || fsinfo[0x3fe] != 0x55 110 || fsinfo[0x3ff] != 0xaa) { 111 pwarn("Invalid signature in fsinfo block\n"); 112 if (ask(0, "Fix")) { 113 memcpy(fsinfo, "RRaA", 4); 114 memcpy(fsinfo + 0x1e4, "rrAa", 4); 115 fsinfo[0x1fc] = fsinfo[0x1fd] = 0; 116 fsinfo[0x1fe] = 0x55; 117 fsinfo[0x1ff] = 0xaa; 118 fsinfo[0x3fc] = fsinfo[0x3fd] = 0; 119 fsinfo[0x3fe] = 0x55; 120 fsinfo[0x3ff] = 0xaa; 121 if (lseek(dosfs, boot->bpbFSInfo * 122 boot->bpbBytesPerSec, SEEK_SET) 123 != boot->bpbFSInfo * boot->bpbBytesPerSec 124 || write(dosfs, fsinfo, sizeof fsinfo) 125 != sizeof fsinfo) { 126 perr("Unable to write bpbFSInfo"); 127 return FSFATAL; 128 } 129 ret = FSBOOTMOD; 130 } else 131 boot->bpbFSInfo = 0; 132 } 133 if (boot->bpbFSInfo) { 134 boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) 135 + (fsinfo[0x1ea] << 16) 136 + (fsinfo[0x1eb] << 24); 137 boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) 138 + (fsinfo[0x1ee] << 16) 139 + (fsinfo[0x1ef] << 24); 140 } 141 142 if (lseek(dosfs, boot->bpbBackup * boot->bpbBytesPerSec, 143 SEEK_SET) 144 != boot->bpbBackup * boot->bpbBytesPerSec 145 || read(dosfs, backup, sizeof backup) != sizeof backup) { 146 perr("could not read backup bootblock"); 147 return FSFATAL; 148 } 149 backup[65] = block[65]; /* XXX */ 150 if (memcmp(block + 11, backup + 11, 79)) { 151 /* 152 * XXX We require a reference that explains 153 * that these bytes need to match, or should 154 * drop the check. gdt@NetBSD has observed 155 * filesystems that work fine under Windows XP 156 * and NetBSD that do not match, so the 157 * requirement is suspect. For now, just 158 * print out useful information and continue. 159 */ 160 pfatal("backup (block %d) mismatch with primary bootblock:\n", 161 boot->bpbBackup); 162 for (i = 11; i < 11 + 90; i++) { 163 if (block[i] != backup[i]) 164 pfatal("\ti=%d\tprimary 0x%02x\tbackup 0x%02x\n", 165 i, block[i], backup[i]); 166 } 167 } 168 /* Check backup bpbFSInfo? XXX */ 169 } 170 171 boot->ClusterOffset = (boot->bpbRootDirEnts * 32 + 172 boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec + 173 boot->bpbResSectors + boot->bpbFATs * boot->FATsecs - 174 CLUST_FIRST * boot->bpbSecPerClust; 175 176 if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE_REAL != 0) { 177 pfatal("Invalid sector size: %u", boot->bpbBytesPerSec); 178 return FSFATAL; 179 } 180 if (boot->bpbSecPerClust == 0) { 181 pfatal("Invalid cluster size: %u", boot->bpbSecPerClust); 182 return FSFATAL; 183 } 184 if (boot->bpbSectors) { 185 boot->bpbHugeSectors = 0; 186 boot->NumSectors = boot->bpbSectors; 187 } else 188 boot->NumSectors = boot->bpbHugeSectors; 189 boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / 190 boot->bpbSecPerClust; 191 192 if (boot->flags&FAT32) 193 boot->ClustMask = CLUST32_MASK; 194 else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) 195 boot->ClustMask = CLUST12_MASK; 196 else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) 197 boot->ClustMask = CLUST16_MASK; 198 else { 199 pfatal("Filesystem too big (%u clusters) for non-FAT32 partition", 200 boot->NumClusters); 201 return FSFATAL; 202 } 203 204 switch (boot->ClustMask) { 205 case CLUST32_MASK: 206 boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4; 207 break; 208 case CLUST16_MASK: 209 boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2; 210 break; 211 default: 212 boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3; 213 break; 214 } 215 216 if (boot->NumFatEntries < boot->NumClusters - CLUST_FIRST) { 217 pfatal("FAT size too small, %u entries won't fit into %u sectors\n", 218 boot->NumClusters, boot->FATsecs); 219 return FSFATAL; 220 } 221 boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust; 222 223 boot->NumFiles = 1; 224 boot->NumFree = 0; 225 226 // @localmod begin 227 // The BSD FAT driver follows the FAT specification of the "dirty bit" 228 // exactly. The Linux FAT driver, however, marks a bit in the boot sector as 229 // "dirty", which is outside the typical FAT specification. For maximum 230 // compatibility, this modification allows fsck to detect (and clean) the 231 // unspecified Linux dirty bit. 232 int dirtybyte = boot->flags&FAT32 ? 0x41 : 0x25; 233 if (block[dirtybyte] & 0x01) { 234 if (ask(1, "MARK BOOT BLOCK CLEAN") == 0) { 235 pwarn("\n***** BOOT BLOCK IS LEFT MARKED AS DIRTY *****\n"); 236 return FSERROR; 237 } 238 pwarn("MARKING BOOT BLOCK CLEAN\n"); 239 block[dirtybyte] &= ~0x01; 240 if (lseek(dosfs, 0, SEEK_SET) != 0 241 || write(dosfs, block, sizeof block) != sizeof block) { 242 perr("could not write boot block"); 243 return FSFATAL; 244 } 245 } 246 // @localmod end 247 248 return ret; 249} 250 251int 252writefsinfo(int dosfs, struct bootblock *boot) 253{ 254 u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; 255 256 if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) 257 != boot->bpbFSInfo * boot->bpbBytesPerSec 258 || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { 259 perr("could not read fsinfo block"); 260 return FSFATAL; 261 } 262 fsinfo[0x1e8] = (u_char)boot->FSFree; 263 fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); 264 fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); 265 fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); 266 fsinfo[0x1ec] = (u_char)boot->FSNext; 267 fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); 268 fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); 269 fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); 270 if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) 271 != boot->bpbFSInfo * boot->bpbBytesPerSec 272 || write(dosfs, fsinfo, sizeof fsinfo) 273 != sizeof fsinfo) { 274 perr("Unable to write bpbFSInfo"); 275 return FSFATAL; 276 } 277 /* 278 * Technically, we should return FSBOOTMOD here. 279 * 280 * However, since Win95 OSR2 (the first M$ OS that has 281 * support for FAT32) doesn't maintain the FSINFO block 282 * correctly, it has to be fixed pretty often. 283 * 284 * Therefor, we handle the FSINFO block only informally, 285 * fixing it if necessary, but otherwise ignoring the 286 * fact that it was incorrect. 287 */ 288 return 0; 289} 290