| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package bootrecord |
| |
| import ( |
| "errors" |
| "fmt" |
| |
| "github.com/golang/glog" |
| |
| "go.fuchsia.dev/fuchsia/src/lib/thinfs/bitops" |
| ) |
| |
| const ( |
| extFlagMaskFAT = 0x0F |
| extFlagMirroringDisabled = 0x80 |
| ) |
| |
| // This file describes the on-disk format for large bootrecords. |
| // The bootrecord exists in the first sector of the partition containing the FAT filesystem. |
| // NOTE: All following structures in this file are stored on disk, which is why they only use the |
| // uint8 type. |
| |
| // brLarge is the prefix of the first sector of the FAT32 filesystem. |
| type brLarge struct { |
| bsPrefix bootsectorPrefix |
| bpb bpbShared |
| bpbExtended bpbLarge |
| bsSuffix bootsectorSuffix |
| padding [420]uint8 // Padding so struct is 512 bytes |
| bootSectorSig [2]uint8 |
| } |
| |
| func (b *brLarge) Validate() error { |
| // The suffix should be validated, but only AFTER determining FAT type. |
| sizeClassLarge := true |
| if err := bootSectorSignatureValid(b.bootSectorSig[:]); err != nil { |
| return err |
| } else if err := b.bsPrefix.validate(); err != nil { |
| return err |
| } else if err := b.bpb.validate(sizeClassLarge); err != nil { |
| return err |
| } else if err := b.bsSuffix.validate(); err != nil { |
| // FAT32-exclusive |
| return err |
| } else if err := b.validateType(); err != nil { |
| // FAT32-exclusive |
| return err |
| } |
| |
| version := b.bpbExtended.FsVersion() |
| if version != 0 { |
| glog.Warning("Loading a FAT32 filesystem with an unexpected version: %d", version) |
| } |
| |
| if b.bpbExtended.SectorsPerFAT32() == 0 { |
| return errors.New("FAT32 must set sectorsPerFAT32 to a nonzero value") |
| } |
| |
| reservedSectors := b.bpb.NumSectorsReserved() |
| backupSector := b.bpbExtended.BackupSector() |
| fsInfoSector := b.bpbExtended.FsInfoSector() |
| if backupSector == 0 || backupSector == fsInfoSector || reservedSectors <= backupSector { |
| return errors.New("Invalid backup boot sector") |
| } else if fsInfoSector == 0 || reservedSectors <= fsInfoSector { |
| return errors.New("Invalid fsInfo sector") |
| } |
| |
| mirroring, primaryIndex := b.bpbExtended.MirroringInfo() |
| if !mirroring && primaryIndex >= b.bpb.NumFATs() { |
| return errors.New("Mirroring disabled, but selected primary FAT is invalid") |
| } |
| |
| return nil |
| } |
| |
| // We made a guess that this bootrecord was FAT32. Double-check that assumption now. |
| func (b *brLarge) validateType() error { |
| if b.bpb.NumRootEntries() != 0 { |
| // Count the number of root directory sectors reserved for FAT32. Should be zero. |
| return errors.New("FAT32 should not reserve space for root directories outside cluster space") |
| } |
| |
| numUsableClusters := b.bpb.TotalClusters(b.FirstDataSector()) |
| if numUsableClusters < 65525 { |
| return fmt.Errorf("Expected at least 65525 clusters for FAT32, but only saw: %d", numUsableClusters) |
| } |
| |
| return nil |
| } |
| |
| func (b *brLarge) FirstDataSector() uint32 { |
| return b.bpb.NumSectorsReserved() + (b.bpb.NumFATs() * b.bpbExtended.SectorsPerFAT32()) |
| } |
| |
| // FAT32-exclusive fields. |
| type bpbLarge struct { |
| sectorsPerFAT32 [4]uint8 // Sectors for ONE FAT |
| extFlags [2]uint8 // Extended flags |
| fsVersion [2]uint8 // High byte: Major revision, Low byte: Minor revision |
| rootCluster [4]uint8 // Cluster number of start of root directory |
| fsInfoSector [2]uint8 // Sector of FS Info structure |
| backupSector [2]uint8 // Sector number of backup copy of boot record |
| reserved2 [12]uint8 // Reserved for future expansion |
| } |
| |
| func (b *bpbLarge) SectorsPerFAT32() uint32 { |
| return uint32(bitops.GetLE32(b.sectorsPerFAT32[:])) |
| } |
| func (b *bpbLarge) ExtFlags() uint32 { |
| return uint32(bitops.GetLE16(b.extFlags[:])) |
| } |
| func (b *bpbLarge) FsVersion() uint32 { |
| return uint32(bitops.GetLE16(b.fsVersion[:])) |
| } |
| func (b *bpbLarge) RootCluster() uint32 { |
| return uint32(bitops.GetLE32(b.rootCluster[:])) |
| } |
| func (b *bpbLarge) FsInfoSector() uint32 { |
| return uint32(bitops.GetLE16(b.fsInfoSector[:])) |
| } |
| func (b *bpbLarge) BackupSector() uint32 { |
| return uint32(bitops.GetLE16(b.backupSector[:])) |
| } |
| |
| func (b *bpbLarge) MirroringInfo() (active bool, primaryIndex uint32) { |
| ext := b.ExtFlags() |
| if ext&extFlagMirroringDisabled == 0 { |
| // Mirroring to all FATs. We are returning "0" as the primary, but any FAT could be used. |
| return true, 0 |
| } |
| // Only a single FAT is active. |
| return false, ext & extFlagMaskFAT |
| } |