diff --git a/bmap.go b/bmap.go index faff06b..ee6459e 100644 --- a/bmap.go +++ b/bmap.go @@ -21,20 +21,23 @@ import ( const defaultBMAPHash = "0000000000000000000000000000000000000000" -type Block struct { +// BlockRange represents a range of blocks of written data +type BlockRange struct { XMLName xml.Name `xml:"Range"` Start int64 End int64 Hash string `xml:"sha1,attr"` } +// Range is an intermediate data type for xml conversion type Range struct { XMLName xml.Name `xml:"Range"` Range string `xml:",chardata"` Hash string `xml:"sha1,attr"` } -func (b *Block) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +// MarshalXML is used to convert BlockRange to a Range object for XML output +func (b *BlockRange) MarshalXML(e *xml.Encoder, start xml.StartElement) error { //fmt.Println(e) //fmt.Println(start) @@ -50,7 +53,8 @@ func (b *Block) MarshalXML(e *xml.Encoder, start xml.StartElement) error { return err } -func (b *Block) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { +// UnmarshalXML is used to convert a Range object from XML input into a BlockRange object +func (b *BlockRange) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { r := Range{} err := d.DecodeElement(&r, &start) @@ -86,10 +90,12 @@ func (b *Block) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { return nil } +// BlockMap represents a list of Block Ranges type BlockMap struct { - Range []Block + Range []BlockRange } +// BMap contains all of the information that needs to be represented in the bmap file type BMap struct { XMLName xml.Name `xml:"bmap"` Version string `xml:"version,attr"` @@ -101,19 +107,23 @@ type BMap struct { BlockMap BlockMap `xml:"BlockMap"` } -func NewBMap(filename string) BMap { +// NewBMap creates a new BMap representation by reading an image file +func NewBMap(filename string) (BMap, error) { stat := syscall.Stat_t{} syscall.Stat(filename, &stat) blockSize := stat.Blksize - fd, _ := os.Open(filename) - + fd, err := os.Open(filename) defer fd.Close() + if err != nil { + return BMap{}, err + } blockMap, total := getBlockMap(fd, blockSize) - return BMap{Version: "1.3", ImageSize: stat.Size, BlockSize: blockSize, BlocksCount: stat.Size / blockSize, MappedBlocksCount: total, BmapFileSHA1: defaultBMAPHash, BlockMap: BlockMap{blockMap}} + return BMap{Version: "1.3", ImageSize: stat.Size, BlockSize: blockSize, BlocksCount: stat.Size / blockSize, MappedBlocksCount: total, BmapFileSHA1: defaultBMAPHash, BlockMap: BlockMap{blockMap}}, nil } +// XMLOutput converts a BMap object to it's corresponding XML output func (b *BMap) XMLOutput() ([]byte, error) { output, err := xml.MarshalIndent(b, "", " ") if err != nil { @@ -122,6 +132,7 @@ func (b *BMap) XMLOutput() ([]byte, error) { return append([]byte(xml.Header), output...), err } +// Write generates initial XML output, takes the SHA signature, and writes updated XML to file func (b *BMap) Write(outputFilename string) error { output, err := b.XMLOutput() if err != nil { @@ -146,6 +157,7 @@ func (b *BMap) Write(outputFilename string) error { return err } +// Load reads and converts a bmap file to a BMap object func Load(filename string) (BMap, error) { b := BMap{} data, err := ioutil.ReadFile(filename) @@ -178,6 +190,7 @@ func Load(filename string) (BMap, error) { return b, err } +// Copy copies an input file and uses the BMap data to create a new image file func (b *BMap) Copy(input string, output string) error { inFile, err := os.Open(input) defer inFile.Close() @@ -276,33 +289,35 @@ func getBZReader(reader io.Reader) (io.ReadSeeker, error) { return bytes.NewReader(data), nil } -func getBlockMap(fd *os.File, blockSize int64) ([]Block, int64) { +// getBlockMap finds all of the ranges of written blocks in a file/image +func getBlockMap(fd *os.File, blockSize int64) ([]BlockRange, int64) { //fd, _ := os.Open(filename) f := fibmap.NewFibmapFile(fd) holes := f.SeekDataHole() - blockMap := make([]Block, len(holes)/2) - currentBlock := Block{} + blockMap := make([]BlockRange, len(holes)/2) + currentBlockRange := BlockRange{} mappedBlocks := int64(0) for i, v := range holes { - if i%2 == 0 { //Block found @ - currentBlock = Block{Start: v / blockSize} + if i%2 == 0 { //BlockRange found @ + currentBlockRange = BlockRange{Start: v / blockSize} } else { // Length of block length := v / blockSize - currentBlock.End = currentBlock.Start + length - 1 + currentBlockRange.End = currentBlockRange.Start + length - 1 h := sha1.New() - fd.Seek(currentBlock.Start*blockSize, 0) + fd.Seek(currentBlockRange.Start*blockSize, 0) io.CopyN(h, fd, v) - currentBlock.Hash = hex.EncodeToString(h.Sum(nil)) + currentBlockRange.Hash = hex.EncodeToString(h.Sum(nil)) - blockMap[i/2] = currentBlock + blockMap[i/2] = currentBlockRange mappedBlocks += length } } return blockMap, mappedBlocks } +// getSHA1Hash returns a hex encoded hash from an io.Reader func getSHA1Hash(r io.Reader) string { h := sha1.New() io.Copy(h, r)