blob: 24f8c11d1f33c8d75d8c7eda0206cb1fc5bc1d5a [file] [log] [blame]
// 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 fat contains the actual File Allocation Table used by the FAT filesystem.
//
// Methods which modify the FAT itself are NOT thread-safe (Allocate / Set / Close), and must be
// write-locked.
//
// Methods which read from the FAT are thread-safe (Get) among themselves, and can be read-locked.
package fat
import (
"errors"
"github.com/golang/glog"
"thinfs/bitops"
"thinfs/fs"
"thinfs/fs/msdosfs/bootrecord"
"thinfs/fs/msdosfs/cluster/fat/fsinfo"
"thinfs/thinio"
)
var (
// ErrHardIO indicates there was an unrecoverable error.
ErrHardIO = errors.New("FAT: Hard Error I/O bit set. Run 'fsck <device> fat' on the partition containing this filesystem")
// ErrDirtyFAT indicates the FAT was not unmounted properly.
ErrDirtyFAT = errors.New("FAT: Dirty volume bit set. Run 'fsck <device> fat' on the partition containing this filesystem")
// ErrNotOpen indicates the FAT cannot be accessed because it is not open.
ErrNotOpen = errors.New("FAT: FAT not open")
// ErrInvalidCluster indicates the provided cluster is invalid.
ErrInvalidCluster = errors.New("FAT: Attempting to access FAT with invalid cluster")
// ErrNoSpace indicates there is no remaining space in the FAT.
ErrNoSpace = fs.ErrNoSpace
)
const (
// Masks for usable cluster numbers.
maskFAT12 = 0x00000FFF
maskFAT16 = 0x0000FFFF
maskFAT32 = 0x0FFFFFFF
// An entry value of "entryEOF..." or higher indicates the end of a file.
eofFAT12 = 0x00000FF8
eofFAT16 = 0x0000FFF8
eofFAT32 = 0x0FFFFFF8
// "BAD CLUSTER" indicates that this cluster is prone to disk errors.
badFAT12 = 0x00000FF7
badFAT16 = 0x0000FFF7
badFAT32 = 0x0FFFFFF7
// The high bits of the entry at FAT[1] are reserved, and contain special values.
dirtyBitFAT16 = 0x00008000 // 1: Volume is "clean". 0: Volume is "dirty", did not dismount properly.
dirtyBitFAT32 = 0x08000000
errorBitFAT16 = 0x00004000 // 1: No disk errors. 0: Disk I/O error encountered; sectors have gone bad.
errorBitFAT32 = 0x04000000
)
// FAT holds the File Allocation Table.
type FAT struct {
device *thinio.Conductor
br *bootrecord.Bootrecord
readonly bool // Are we preventing writes to the FAT?
mirror bool // Are we mirroring to multiple FATs?
primary uint32 // Which FAT are we using?
numFATs uint32 // How many FATs are we mirroring to?
closed bool
// FAT32-exclusive FS Info values
fsInfoValid bool
freeCount uint32 // TODO(smklein): Use this value
nextFree uint32
}
// Open opens a new File Allocation Table. This FAT will NOT be thread-safe; thread safety will
// need to be implemented by the layer above the FAT.
//
// If the filesystem is writeable, also marks the dirty bit as "dirty".
func Open(d *thinio.Conductor, br *bootrecord.Bootrecord, readonly bool) (*FAT, error) {
glog.V(2).Info("Creating new FAT")
mirror, numFATs, primary := br.MirroringInfo()
f := &FAT{
device: d,
br: br,
readonly: readonly,
mirror: mirror,
primary: primary,
numFATs: numFATs,
}
if f.isHardError() {
return nil, ErrHardIO
} else if f.isDirty() {
return nil, ErrDirtyFAT
}
if !f.readonly {
glog.V(2).Info("Setting FAT as dirty (writeable)")
if err := f.setDirty(true); err != nil {
return nil, err
}
// If the dirty bit is not flushed, it is possible for parts of the filesytem (other than
// the dirty bit) to reach persistent storage first, which can make a dirty filesystem
// incorrectly appear clean.
f.device.Flush()
}
// Gather the FS Info values (if they exist).
if fsInfoOffset, err := f.br.FsInfoOffset(); err == nil {
if f.freeCount, f.nextFree, err = fsinfo.ReadHints(f.device, fsInfoOffset); err == nil {
f.fsInfoValid = true
}
}
if !f.fsInfoValid {
f.freeCount = fsinfo.UnknownFlag
f.nextFree = bootrecord.NumReservedClusters
}
return f, nil
}
// Close closes a File Allocation Table.
//
// If the filesystem was writeable, this marks the dirty bit as "clean".
func (f *FAT) Close() error {
glog.V(2).Info("Closing FAT")
if f.closed {
return ErrNotOpen
} else if !f.readonly {
glog.V(2).Info("Setting FAT dirty status to clean")
f.setDirty(false)
if fsInfoOffset, err := f.br.FsInfoOffset(); err == nil && f.fsInfoValid {
// Errors are ignored here intentionally -- if, for some reason, we are unable to write
// "FreeCount" or "NextFree", we still want to flush the device and mark the FAT
// structure as closed.
fsinfo.WriteFreeCount(f.device, fsInfoOffset, f.freeCount)
fsinfo.WriteNextFree(f.device, fsInfoOffset, f.nextFree)
}
f.device.Flush()
}
f.closed = true
return nil
}
// EOFValue returns the FAT value that means "end of file".
// Notably, there are multiple values that mean EOF to FAT.
func (f *FAT) EOFValue() uint32 {
switch f.br.Type() {
case bootrecord.FAT32:
return eofFAT32
case bootrecord.FAT16:
return eofFAT16
case bootrecord.FAT12:
return eofFAT12
default:
panic("Unhandled FAT version")
}
}
// FreeValue returns the FAT value that means "free cluster".
func (f *FAT) FreeValue() uint32 {
return 0
}
// IsEOF describes if the entry signifies "End of File".
func (f *FAT) IsEOF(e uint32) bool {
switch f.br.Type() {
case bootrecord.FAT32:
return e >= eofFAT32
case bootrecord.FAT16:
return e >= eofFAT16
case bootrecord.FAT12:
return e >= eofFAT12
default:
panic("Unhandled FAT version")
}
}
// IsFree describes if the entry signifies "free cluster".
func (f *FAT) IsFree(e uint32) bool {
return e == 0
}
// SetHardError marks that the volume has encountered a disk I/O error.
func (f *FAT) SetHardError() error {
// Error Bit exists in FAT[1].
if f.closed {
return ErrNotOpen
} else if f.readonly {
return fs.ErrReadOnly
}
offset := f.br.ClusterLocationFATPrimary(1)
v, err := f.getRawEntry(offset)
if err != nil {
return err
}
// Clearing the bit marks the volume as erroneous.
v &^= f.errorBit()
return f.setRawEntry(v, offset)
}
// Get gets the value of a cluster entry.
// Can only be used to access clusters which are not reserved.
// Returns an error if accessing out-of-bounds cluster.
func (f *FAT) Get(cluster uint32) (uint32, error) {
if f.closed {
return 0, ErrNotOpen
} else if !f.br.ClusterInValidRange(cluster) {
return 0, ErrInvalidCluster
}
offset := int64(f.br.ClusterLocationFATPrimary(cluster))
entry, err := f.getRawEntry(offset)
if err != nil {
return 0, err
}
return (entry & f.clusterMask()), nil
}
// Allocate returns the next known free cluster, starting from cluster "start".
// If a cluster is found, it is set to an EOF value (instead of free).
//
// If start is invalid, it is ignored. If no entries remain, an error is returned.
func (f *FAT) Allocate() (uint32, error) {
if f.closed {
return 0, ErrNotOpen
} else if f.readonly {
return 0, fs.ErrReadOnly
}
glog.V(2).Infof("Allocating from: %x\n", f.nextFree)
isFreeAt := func(cluster uint32) bool {
entry, err := f.Get(cluster)
if err != nil {
panic("Allocate not checking bounds properly")
} else if f.IsFree(entry) && !f.isBad(cluster) {
// The entry must be free, but we also should ensure we don't allocate the entry which
// means "BAD CLUSTER".
return true
}
return false
}
minCluster := bootrecord.NumReservedClusters
maxCluster := minCluster + f.br.NumUsableClusters()
if !f.br.ClusterInValidRange(f.nextFree) {
// Ignore start if invalid (somehow).
f.nextFree = minCluster
}
start := f.nextFree
for ; f.nextFree < maxCluster; f.nextFree++ {
if isFreeAt(f.nextFree) {
return f.nextFree, f.Set(f.EOFValue(), f.nextFree)
}
}
for f.nextFree = minCluster; f.nextFree < start; f.nextFree++ {
if isFreeAt(f.nextFree) {
return f.nextFree, f.Set(f.EOFValue(), f.nextFree)
}
}
return 0, ErrNoSpace
}
// Set sets the value of a cluster entry.
//
// Returns an error if accessing an out-of-bounds cluster, or setting a cluster to point to itself.
func (f *FAT) Set(value uint32, cluster uint32) error {
if f.closed {
return ErrNotOpen
} else if f.readonly {
return fs.ErrReadOnly
}
glog.V(2).Infof("Setting cluster %x to value %x\n", cluster, value)
if !f.br.ClusterInValidRange(cluster) {
return ErrInvalidCluster
} else if value == cluster {
// FAT cluster cannot point to itself; this creates a loop.
return ErrInvalidCluster
}
// Write to the primary first. Only return an error if the write to the primary fails.
offset := int64(f.br.ClusterLocationFATPrimary(cluster))
status, err := f.setValueAt(value, offset)
if err != nil {
return err
}
// Update the number of free clusters (if it has changed).
if status == becameFree {
f.freeCount++
} else if status == becameAllocated {
f.freeCount--
}
if f.mirror {
// Mirroring mandates we write to multiple FATs at the same time.
// Don't bother checking errors here -- what would we even do if a write to the primary
// succeeded, but a write to the backup FAT failed?
for indexFAT := uint32(0); indexFAT < f.numFATs; indexFAT++ {
if indexFAT != f.primary {
offset := int64(f.br.ClusterLocationFAT(indexFAT, cluster))
f.setValueAt(value, offset)
}
}
}
return nil
}
func (f *FAT) clusterMask() uint32 {
switch f.br.Type() {
case bootrecord.FAT32:
return maskFAT32
case bootrecord.FAT16:
return maskFAT16
default:
panic("Unhandled FAT version")
}
}
func (f *FAT) dirtyBit() uint32 {
switch f.br.Type() {
case bootrecord.FAT32:
return dirtyBitFAT32
case bootrecord.FAT16:
return dirtyBitFAT16
default:
panic("Unhandled FAT version")
}
}
func (f *FAT) errorBit() uint32 {
switch f.br.Type() {
case bootrecord.FAT32:
return errorBitFAT32
case bootrecord.FAT16:
return errorBitFAT16
default:
panic("Unhandled FAT version")
}
}
// getRawEntry accesses the full entry (appropriate for FAT type) and returns it as-is.
func (f *FAT) getRawEntry(offset int64) (uint32, error) {
glog.V(2).Infof("Getting raw entry at %d", offset)
buf := make([]byte, f.br.FATEntrySize())
_, err := f.device.ReadAt(buf, offset)
if err != nil {
return 0, err
}
switch f.br.Type() {
case bootrecord.FAT32:
return bitops.GetLE32(buf), nil
case bootrecord.FAT16:
return uint32(bitops.GetLE16(buf)), nil
default:
panic("Unknown FAT type")
}
}
// setRawEntry accesses the full entry (appropriate for FAT type) and sets it as-is.
func (f *FAT) setRawEntry(value uint32, offset int64) error {
glog.V(2).Infof("Setting raw entry at %d", offset)
buf := make([]byte, f.br.FATEntrySize())
switch f.br.Type() {
case bootrecord.FAT32:
bitops.PutLE32(buf, uint32(value))
case bootrecord.FAT16:
bitops.PutLE16(buf, uint16(value))
default:
panic("Unknown FAT type")
}
_, err := f.device.WriteAt(buf, offset)
return err
}
// Status identifying the change between free / allocated for a particular cluster. This helps keep
// track of the total number of free vs allocated clusters.
type freeStatus int
const (
becameFree freeStatus = iota
becameAllocated
unchanged
)
// Internal "set", which sets a FAT entry at a known offset in the disk.
// Returns the freeStatus, providing information about the number of free vs allocated clusters.
func (f *FAT) setValueAt(value uint32, offset int64) (freeStatus, error) {
v, err := f.getRawEntry(offset)
if err != nil {
return unchanged, err
}
freeBefore := f.IsFree(v & f.clusterMask())
freeAfter := f.IsFree(value)
if f.isBad(v & f.clusterMask()) {
// This cluster value indicates "bad sector", and cannot be used.
return unchanged, ErrInvalidCluster
}
// Preserve the top bits -- they are reserved.
v &= ^f.clusterMask()
v |= f.clusterMask() & value
if err := f.setRawEntry(v, offset); err != nil {
return unchanged, err
}
status := unchanged
if freeBefore && !freeAfter {
status = becameAllocated
} else if !freeBefore && freeAfter {
status = becameFree
}
return status, nil
}
// isBad describes if the entry points to an unallocatable sector.
func (f *FAT) isBad(e uint32) bool {
switch f.br.Type() {
case bootrecord.FAT32:
return e == badFAT32
case bootrecord.FAT16:
return e == badFAT16
case bootrecord.FAT12:
return e == badFAT12
default:
panic("Unhandled FAT version")
}
}
// setDirty marks that the volume has been mounted and may be in a modified state.
func (f *FAT) setDirty(m bool) error {
if f.br.Type() == bootrecord.FAT12 {
return nil
} else if err := f.setDirtyLinux(m); err != nil {
return err
} else if err := f.setDirtyBSD(m); err != nil {
return err
}
return nil
}
func (f *FAT) dirtyByteOffsetLinux() int64 {
// NOTE: An undocumented 'dirty bit' exists in the bottom bit of either:
// 1) Byte 0x41 (FAT32), or
// 2) Byte 0x25 (FAT16) of the bootsector.
if f.br.Type() == bootrecord.FAT32 {
return 0x41
}
return 0x25
}
// Linux and BSD use distinct bits to represent that the FAT filesystem is dirty. To be as
// conservative as possible, flip both bits to dirty when mounting a filesystem, and flip them both
// to clean when unmounting.
func (f *FAT) setDirtyLinux(m bool) error {
offset := f.dirtyByteOffsetLinux()
var buf [1]byte
if _, err := f.device.ReadAt(buf[:], offset); err != nil {
return err
} else if m { // Setting Dirty
buf[0] |= 0x01
} else { // Setting Clean
buf[0] &^= 0x01
}
_, err := f.device.WriteAt(buf[:], offset)
return err
}
func (f *FAT) setDirtyBSD(m bool) error {
offset := f.br.ClusterLocationFATPrimary(1)
v, err := f.getRawEntry(offset)
if err != nil {
return err
} else if m { // Clearing the bit marks the volume as dirty
v &^= f.dirtyBit()
} else { // Setting the bit marks the volume as clean
v |= f.dirtyBit()
}
return f.setRawEntry(v, offset)
}
// isDirty returns true if the volume is dirty (i.e., it was not dismounted properly).
func (f *FAT) isDirty() bool {
if f.br.Type() == bootrecord.FAT12 {
return false
}
return f.isDirtyLinux() || f.isDirtyBSD()
}
func (f *FAT) isDirtyLinux() bool {
offset := f.dirtyByteOffsetLinux()
var buf [1]byte
_, err := f.device.ReadAt(buf[:], offset)
if err != nil {
// If we can't identify the dirty bit, assume the volume is dirty.
return true
}
return (buf[0] & 0x01) != 0
}
func (f *FAT) isDirtyBSD() bool {
// Dirty Bit exists in FAT[1]
offset := f.br.ClusterLocationFATPrimary(1)
v, err := f.getRawEntry(offset)
if err != nil {
// If we can't identify the dirty bit, assume the volume is dirty.
return true
}
return (v & f.dirtyBit()) == 0
}
// isHardError returns true if a disk I/O error occurred the last time the volume was mounted.
func (f *FAT) isHardError() bool {
if f.br.Type() == bootrecord.FAT12 {
return false
}
// Error Bit exists in FAT[1].
offset := f.br.ClusterLocationFATPrimary(1)
v, err := f.getRawEntry(offset)
if err != nil {
// If we can't identify the error bit, assume there is was an I/O error.
return true
}
return (v & f.errorBit()) == 0
}
// FreeCount returns the number of free clusters stored in fsinfo.
func (f *FAT) FreeCount() (int64, error) {
err := error(nil)
if f.freeCount == fsinfo.UnknownFlag {
err = fs.ErrNotFound
}
return int64(f.freeCount), err
}