179455Sobrien/* 279455Sobrien * Copyright (C) 1995, 1997 Wolfgang Solfrank 379455Sobrien * Copyright (c) 1995 Martin Husemann 479455Sobrien * 579455Sobrien * Redistribution and use in source and binary forms, with or without 679455Sobrien * modification, are permitted provided that the following conditions 779455Sobrien * are met: 879455Sobrien * 1. Redistributions of source code must retain the above copyright 979455Sobrien * notice, this list of conditions and the following disclaimer. 1079455Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1179455Sobrien * notice, this list of conditions and the following disclaimer in the 1279455Sobrien * documentation and/or other materials provided with the distribution. 1379455Sobrien * 1479455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 1579455Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1679455Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1779455Sobrien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1879455Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1979455Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2079455Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2179455Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2279455Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2379455Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2479455Sobrien */ 2579455Sobrien 2679455Sobrien 2779455Sobrien#include <sys/cdefs.h> 2879455Sobrien#ifndef lint 29128463Stjr__RCSID("$NetBSD: boot.c,v 1.9 2003/07/24 19:25:46 ws Exp $"); 3079455Sobrienstatic const char rcsid[] = 3179455Sobrien "$FreeBSD$"; 3279455Sobrien#endif /* not lint */ 3379455Sobrien 3479455Sobrien#include <stdlib.h> 3579455Sobrien#include <string.h> 3679455Sobrien#include <stdio.h> 3779455Sobrien#include <unistd.h> 3879455Sobrien 3979455Sobrien#include "ext.h" 4079455Sobrien#include "fsutil.h" 4179455Sobrien 4279455Sobrienint 43203872Skibreadboot(int dosfs, struct bootblock *boot) 4479455Sobrien{ 4579455Sobrien u_char block[DOSBOOTBLOCKSIZE]; 4679455Sobrien u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; 4779455Sobrien u_char backup[DOSBOOTBLOCKSIZE]; 4879455Sobrien int ret = FSOK; 49203872Skib int i; 5079455Sobrien 51209364Sbrian if (read(dosfs, block, sizeof block) != sizeof block) { 5279455Sobrien perror("could not read boot block"); 5379455Sobrien return FSFATAL; 5479455Sobrien } 5579455Sobrien 5679455Sobrien if (block[510] != 0x55 || block[511] != 0xaa) { 57209364Sbrian pfatal("Invalid signature in boot block: %02x%02x", 58209364Sbrian block[511], block[510]); 5979455Sobrien return FSFATAL; 6079455Sobrien } 6179455Sobrien 6279455Sobrien memset(boot, 0, sizeof *boot); 6379455Sobrien boot->ValidFat = -1; 6479455Sobrien 6579455Sobrien /* decode bios parameter block */ 66203874Skib boot->bpbBytesPerSec = block[11] + (block[12] << 8); 67203874Skib boot->bpbSecPerClust = block[13]; 68203874Skib boot->bpbResSectors = block[14] + (block[15] << 8); 69203874Skib boot->bpbFATs = block[16]; 70203874Skib boot->bpbRootDirEnts = block[17] + (block[18] << 8); 71203874Skib boot->bpbSectors = block[19] + (block[20] << 8); 72203874Skib boot->bpbMedia = block[21]; 73203874Skib boot->bpbFATsmall = block[22] + (block[23] << 8); 7479455Sobrien boot->SecPerTrack = block[24] + (block[25] << 8); 75203874Skib boot->bpbHeads = block[26] + (block[27] << 8); 76209364Sbrian boot->bpbHiddenSecs = block[28] + (block[29] << 8) + 77209364Sbrian (block[30] << 16) + (block[31] << 24); 78209364Sbrian boot->bpbHugeSectors = block[32] + (block[33] << 8) + 79209364Sbrian (block[34] << 16) + (block[35] << 24); 8079455Sobrien 81203874Skib boot->FATsecs = boot->bpbFATsmall; 8279455Sobrien 83203874Skib if (!boot->bpbRootDirEnts) 8479455Sobrien boot->flags |= FAT32; 8579455Sobrien if (boot->flags & FAT32) { 8679455Sobrien boot->FATsecs = block[36] + (block[37] << 8) 8779455Sobrien + (block[38] << 16) + (block[39] << 24); 8879455Sobrien if (block[40] & 0x80) 8979455Sobrien boot->ValidFat = block[40] & 0x0f; 9079455Sobrien 9179455Sobrien /* check version number: */ 9279455Sobrien if (block[42] || block[43]) { 9379455Sobrien /* Correct? XXX */ 94102231Strhodes pfatal("Unknown file system version: %x.%x", 9579455Sobrien block[43], block[42]); 9679455Sobrien return FSFATAL; 9779455Sobrien } 98203874Skib boot->bpbRootClust = block[44] + (block[45] << 8) 9979455Sobrien + (block[46] << 16) + (block[47] << 24); 100203874Skib boot->bpbFSInfo = block[48] + (block[49] << 8); 101203874Skib boot->bpbBackup = block[50] + (block[51] << 8); 10279455Sobrien 103209364Sbrian if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, 104209364Sbrian SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec 105209364Sbrian || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { 10679455Sobrien perror("could not read fsinfo block"); 10779455Sobrien return FSFATAL; 10879455Sobrien } 10979455Sobrien if (memcmp(fsinfo, "RRaA", 4) 11079455Sobrien || memcmp(fsinfo + 0x1e4, "rrAa", 4) 11179455Sobrien || fsinfo[0x1fc] 11279455Sobrien || fsinfo[0x1fd] 11379455Sobrien || fsinfo[0x1fe] != 0x55 11479455Sobrien || fsinfo[0x1ff] != 0xaa 11579455Sobrien || fsinfo[0x3fc] 11679455Sobrien || fsinfo[0x3fd] 11779455Sobrien || fsinfo[0x3fe] != 0x55 11879455Sobrien || fsinfo[0x3ff] != 0xaa) { 119175853Syar pwarn("Invalid signature in fsinfo block\n"); 120175853Syar if (ask(0, "Fix")) { 12179455Sobrien memcpy(fsinfo, "RRaA", 4); 12279455Sobrien memcpy(fsinfo + 0x1e4, "rrAa", 4); 12379455Sobrien fsinfo[0x1fc] = fsinfo[0x1fd] = 0; 12479455Sobrien fsinfo[0x1fe] = 0x55; 12579455Sobrien fsinfo[0x1ff] = 0xaa; 12679455Sobrien fsinfo[0x3fc] = fsinfo[0x3fd] = 0; 12779455Sobrien fsinfo[0x3fe] = 0x55; 12879455Sobrien fsinfo[0x3ff] = 0xaa; 129209364Sbrian if (lseek(dosfs, boot->bpbFSInfo * 130209364Sbrian boot->bpbBytesPerSec, SEEK_SET) 131203874Skib != boot->bpbFSInfo * boot->bpbBytesPerSec 13279455Sobrien || write(dosfs, fsinfo, sizeof fsinfo) 13379455Sobrien != sizeof fsinfo) { 134203874Skib perror("Unable to write bpbFSInfo"); 13579455Sobrien return FSFATAL; 13679455Sobrien } 13779455Sobrien ret = FSBOOTMOD; 13879455Sobrien } else 139203874Skib boot->bpbFSInfo = 0; 14079455Sobrien } 141203874Skib if (boot->bpbFSInfo) { 14279455Sobrien boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) 14379455Sobrien + (fsinfo[0x1ea] << 16) 14479455Sobrien + (fsinfo[0x1eb] << 24); 14579455Sobrien boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) 14679455Sobrien + (fsinfo[0x1ee] << 16) 14779455Sobrien + (fsinfo[0x1ef] << 24); 14879455Sobrien } 14979455Sobrien 150209364Sbrian if (lseek(dosfs, boot->bpbBackup * boot->bpbBytesPerSec, 151209364Sbrian SEEK_SET) 152203874Skib != boot->bpbBackup * boot->bpbBytesPerSec 15379455Sobrien || read(dosfs, backup, sizeof backup) != sizeof backup) { 15479455Sobrien perror("could not read backup bootblock"); 15579455Sobrien return FSFATAL; 15679455Sobrien } 157128463Stjr backup[65] = block[65]; /* XXX */ 158128463Stjr if (memcmp(block + 11, backup + 11, 79)) { 159203872Skib /* 160203872Skib * XXX We require a reference that explains 161203872Skib * that these bytes need to match, or should 162203872Skib * drop the check. gdt@NetBSD has observed 163203872Skib * filesystems that work fine under Windows XP 164203872Skib * and NetBSD that do not match, so the 165203872Skib * requirement is suspect. For now, just 166203872Skib * print out useful information and continue. 167203872Skib */ 168203872Skib pfatal("backup (block %d) mismatch with primary bootblock:\n", 169203874Skib boot->bpbBackup); 170203872Skib for (i = 11; i < 11 + 90; i++) { 171203872Skib if (block[i] != backup[i]) 172203872Skib pfatal("\ti=%d\tprimary 0x%02x\tbackup 0x%02x\n", 173203872Skib i, block[i], backup[i]); 174203872Skib } 17579455Sobrien } 176203874Skib /* Check backup bpbFSInfo? XXX */ 17779455Sobrien } 17879455Sobrien 179209364Sbrian boot->ClusterOffset = (boot->bpbRootDirEnts * 32 + 180209364Sbrian boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec + 181209364Sbrian boot->bpbResSectors + boot->bpbFATs * boot->FATsecs - 182209364Sbrian CLUST_FIRST * boot->bpbSecPerClust; 18379455Sobrien 184203874Skib if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE != 0) { 185203874Skib pfatal("Invalid sector size: %u", boot->bpbBytesPerSec); 18679455Sobrien return FSFATAL; 18779455Sobrien } 188203874Skib if (boot->bpbSecPerClust == 0) { 189203874Skib pfatal("Invalid cluster size: %u", boot->bpbSecPerClust); 19079455Sobrien return FSFATAL; 19179455Sobrien } 192203874Skib if (boot->bpbSectors) { 193203874Skib boot->bpbHugeSectors = 0; 194203874Skib boot->NumSectors = boot->bpbSectors; 19579455Sobrien } else 196203874Skib boot->NumSectors = boot->bpbHugeSectors; 197209364Sbrian boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / 198209364Sbrian boot->bpbSecPerClust; 19979455Sobrien 20079455Sobrien if (boot->flags&FAT32) 20179455Sobrien boot->ClustMask = CLUST32_MASK; 20279455Sobrien else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) 20379455Sobrien boot->ClustMask = CLUST12_MASK; 20479455Sobrien else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) 20579455Sobrien boot->ClustMask = CLUST16_MASK; 20679455Sobrien else { 20779455Sobrien pfatal("Filesystem too big (%u clusters) for non-FAT32 partition", 20879455Sobrien boot->NumClusters); 20979455Sobrien return FSFATAL; 21079455Sobrien } 21179455Sobrien 21279455Sobrien switch (boot->ClustMask) { 21379455Sobrien case CLUST32_MASK: 214203874Skib boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4; 21579455Sobrien break; 21679455Sobrien case CLUST16_MASK: 217203874Skib boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2; 21879455Sobrien break; 21979455Sobrien default: 220203874Skib boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3; 22179455Sobrien break; 22279455Sobrien } 22379455Sobrien 22479455Sobrien if (boot->NumFatEntries < boot->NumClusters) { 22579455Sobrien pfatal("FAT size too small, %u entries won't fit into %u sectors\n", 22679455Sobrien boot->NumClusters, boot->FATsecs); 22779455Sobrien return FSFATAL; 22879455Sobrien } 229203874Skib boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust; 23079455Sobrien 23179455Sobrien boot->NumFiles = 1; 23279455Sobrien boot->NumFree = 0; 23379455Sobrien 23479455Sobrien return ret; 23579455Sobrien} 23679455Sobrien 23779455Sobrienint 238203872Skibwritefsinfo(int dosfs, struct bootblock *boot) 23979455Sobrien{ 24079455Sobrien u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; 24179455Sobrien 242203874Skib if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) 243203874Skib != boot->bpbFSInfo * boot->bpbBytesPerSec 24479455Sobrien || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { 24579455Sobrien perror("could not read fsinfo block"); 24679455Sobrien return FSFATAL; 24779455Sobrien } 24879455Sobrien fsinfo[0x1e8] = (u_char)boot->FSFree; 24979455Sobrien fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); 25079455Sobrien fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); 25179455Sobrien fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); 25279455Sobrien fsinfo[0x1ec] = (u_char)boot->FSNext; 25379455Sobrien fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); 25479455Sobrien fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); 25579455Sobrien fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); 256203874Skib if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) 257203874Skib != boot->bpbFSInfo * boot->bpbBytesPerSec 25879455Sobrien || write(dosfs, fsinfo, sizeof fsinfo) 25979455Sobrien != sizeof fsinfo) { 260203874Skib perror("Unable to write bpbFSInfo"); 26179455Sobrien return FSFATAL; 26279455Sobrien } 26379455Sobrien /* 26479455Sobrien * Technically, we should return FSBOOTMOD here. 26579455Sobrien * 26679455Sobrien * However, since Win95 OSR2 (the first M$ OS that has 26779455Sobrien * support for FAT32) doesn't maintain the FSINFO block 26879455Sobrien * correctly, it has to be fixed pretty often. 26979455Sobrien * 27079455Sobrien * Therefor, we handle the FSINFO block only informally, 271128463Stjr * fixing it if necessary, but otherwise ignoring the 27279455Sobrien * fact that it was incorrect. 27379455Sobrien */ 27479455Sobrien return 0; 27579455Sobrien} 276