package fat import ( "io" "math" "github.com/mitchellh/go-fs" ) type ClusterChain struct { device fs.BlockDevice fat *FAT startCluster uint32 readOffset uint32 writeOffset uint32 } func (c *ClusterChain) Read(p []byte) (n int, err error) { bpc := c.fat.bs.BytesPerCluster() chain := c.fat.Chain(c.startCluster) dataOffset := uint32(0) for dataOffset < uint32(len(p)) { chainIdx := c.readOffset / bpc if int(chainIdx) >= len(chain) { err = io.EOF return } clusterOffset := c.fat.bs.ClusterOffset(int(chain[chainIdx])) clusterOffset += c.readOffset % bpc dataOffsetEnd := dataOffset + bpc dataOffsetEnd -= c.readOffset % bpc dataOffsetEnd = uint32(math.Min(float64(dataOffsetEnd), float64(len(p)))) var nw int nw, err = c.device.ReadAt(p[dataOffset:dataOffsetEnd], int64(clusterOffset)) if err != nil { return } c.readOffset += uint32(nw) dataOffset += uint32(nw) n += nw } return } // Write will write to the cluster chain, expanding it if necessary. func (c *ClusterChain) Write(p []byte) (n int, err error) { bpc := c.fat.bs.BytesPerCluster() chain := c.fat.Chain(c.startCluster) chainLength := uint32(len(chain)) * bpc if chainLength < c.writeOffset+uint32(len(p)) { // We need to grow the chain bytesNeeded := (c.writeOffset + uint32(len(p))) - chainLength clustersNeeded := int(math.Ceil(float64(bytesNeeded) / float64(bpc))) chain, err = c.fat.ResizeChain(c.startCluster, len(chain)+clustersNeeded) if err != nil { return } // Write the FAT out if err = c.fat.WriteToDevice(c.device); err != nil { return } } dataOffset := uint32(0) for dataOffset < uint32(len(p)) { chainIdx := c.writeOffset / bpc clusterOffset := c.fat.bs.ClusterOffset(int(chain[chainIdx])) clusterOffset += c.writeOffset % bpc dataOffsetEnd := dataOffset + bpc dataOffsetEnd -= c.writeOffset % bpc dataOffsetEnd = uint32(math.Min(float64(dataOffsetEnd), float64(len(p)))) var nw int nw, err = c.device.WriteAt(p[dataOffset:dataOffsetEnd], int64(clusterOffset)) if err != nil { return } c.writeOffset += uint32(nw) dataOffset += uint32(nw) n += nw } return }