blob: 8a69e72a0c8878a3416b00aa487c7a6c9acba644 [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 fsinfo describes the FAT32-exclusive FSInfo structure
package fsinfo
import (
"errors"
"fmt"
"unsafe"
"thinfs/bitops"
"thinfs/thinio"
)
const (
freeCountOffset = 488
nextFreeClusterOffset = 492
leadSig = 0x41615252
innerSig = 0x61417272
finalSig = 0xAA550000
// UnknownFlag describes a value (such as the next free cluster) which is not known.
UnknownFlag = 0xFFFFFFFF
)
// fsInfo describes the FAT32 FSInfo block.
type fsInfo struct {
leadSignature [4]uint8 // 0x41615252
reserved1 [480]uint8 // Zero
innerSignature [4]uint8 // 0x61417272
freeCountClusters [4]uint8 // The last known count of free clusters or 0xFFFFFFFF
nextFreeCluster [4]uint8 // A hint for the FAT driver or 0xFFFFFFFF
reserved2 [12]uint8 // Zero
finalSignature [4]uint8 // 0xAA550000
}
// Validate verifies the FsInfo is valid.
func (f *fsInfo) validate() error {
// Validate signatures
s := bitops.GetLE32(f.leadSignature[:])
if s != leadSig {
return fmt.Errorf("Expected leading signature: %d, but got %d", leadSig, s)
}
s = bitops.GetLE32(f.innerSignature[:])
if s != innerSig {
return fmt.Errorf("Expected inner signature: %d, but got %d", innerSig, s)
}
s = bitops.GetLE32(f.finalSignature[:])
if s != finalSig {
return fmt.Errorf("Expected final signature: %d, but got %d", finalSig, s)
}
return nil
}
// ReadHints reads FS Info from the device (at the offset) and returns the freeCount of clusters and the
// nextFree cluster (if they are known). Otherwise, returns an error.
func ReadHints(d *thinio.Conductor, offset int64) (freeCount, nextFree uint32, err error) {
var info fsInfo
b := (*(*[unsafe.Sizeof(info)]byte)(unsafe.Pointer(&info)))[:]
_, err = d.ReadAt(b, offset)
if err != nil {
return 0, 0, err
}
if err = info.validate(); err != nil {
return 0, 0, err
}
freeCount = bitops.GetLE32(info.freeCountClusters[:])
nextFree = bitops.GetLE32(info.nextFreeCluster[:])
if freeCount == UnknownFlag {
return 0, 0, errors.New("Unknown free count of clusters")
} else if nextFree == UnknownFlag {
return 0, 0, errors.New("Unknown next free cluster")
}
return
}
// WriteFreeCount takes the offset of the FS Info structure and updates the on-disk value of the
// "free cluster count".
func WriteFreeCount(d *thinio.Conductor, offset int64, freeCount uint32) error {
b := make([]byte, 4)
bitops.PutLE32(b, freeCount)
_, err := d.WriteAt(b, offset+freeCountOffset)
return err
}
// WriteNextFree takes the offset of the FS Info structure and updates the on-disk value of the
// "next free cluster".
func WriteNextFree(d *thinio.Conductor, offset int64, nextFree uint32) error {
b := make([]byte, 4)
bitops.PutLE32(b, nextFree)
_, err := d.WriteAt(b, offset+nextFreeClusterOffset)
return err
}