| // 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 |
| } |