157 lines
3.1 KiB
Go
157 lines
3.1 KiB
Go
package internal
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type HashActions interface {
|
|
GetFolderHash(path string) (string, error)
|
|
CompareReleaseHash(path string, hash string) error
|
|
AddHash(path, hash string) error
|
|
ReplaceHash(path, hash string) error
|
|
RemoveHash(path string) error
|
|
writeHashTable() error
|
|
}
|
|
|
|
type Hash struct {
|
|
hashTable map[string]string
|
|
hashFile string
|
|
}
|
|
|
|
// NewHash returns a new Hash object. It reads the hashes from the ~/.local/doggofetcher/hashes.txt file then loads
|
|
// them into the hash table.
|
|
func NewHash(hashFile string) (HashActions, error) {
|
|
f, err := os.Open(hashFile)
|
|
if err != nil {
|
|
return &Hash{}, err
|
|
}
|
|
defer f.Close()
|
|
|
|
hashTable := make(map[string]string)
|
|
sc := bufio.NewScanner(f)
|
|
for sc.Scan() {
|
|
l := strings.Split(sc.Text(), " ")
|
|
hashTable[l[0]] = l[1]
|
|
}
|
|
|
|
return &Hash{
|
|
hashTable: hashTable,
|
|
hashFile: hashFile,
|
|
}, nil
|
|
}
|
|
|
|
// GetFolderHash returns the hash of the folder by using the Merkle tree.
|
|
func (h *Hash) GetFolderHash(path string) (string, error) {
|
|
hashes := [][]byte{}
|
|
err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !d.Type().IsRegular() {
|
|
return nil
|
|
}
|
|
if d.IsDir() {
|
|
hash, err := h.GetFolderHash(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hashes = append(hashes, []byte(hash))
|
|
return nil
|
|
}
|
|
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
sha := sha256.New()
|
|
buf := make([]byte, 10*1024)
|
|
for {
|
|
n, err := f.Read(buf)
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
|
|
sha.Write(buf[:n])
|
|
}
|
|
|
|
hashes = append(hashes, sha.Sum(nil))
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
sha := sha256.New()
|
|
for _, h := range hashes {
|
|
if _, err := sha.Write(h); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return hex.EncodeToString(sha.Sum(nil)), nil
|
|
}
|
|
|
|
// CompareReleaseHash compares the hash of the release with the hash in the hash table.
|
|
func (h *Hash) CompareReleaseHash(path, hash string) error {
|
|
if h, ok := h.hashTable[path]; !ok {
|
|
return ErrHashNotFound
|
|
} else if h != hash {
|
|
return ErrHashInvalid
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// AddHash adds a hash to the hash table by writing the "hashTable" property and the file.
|
|
func (h *Hash) AddHash(path, hash string) error {
|
|
h.hashTable[path] = hash
|
|
|
|
return h.writeHashTable()
|
|
}
|
|
|
|
// ReplaceHash replaces the hash in the hash table with the new hash.
|
|
func (h *Hash) ReplaceHash(path, hash string) error {
|
|
h.hashTable[path] = hash
|
|
|
|
return h.writeHashTable()
|
|
}
|
|
|
|
// RemoveHash removes a hash from the hash table.
|
|
func (h *Hash) RemoveHash(path string) error {
|
|
delete(h.hashTable, path)
|
|
|
|
return h.writeHashTable()
|
|
}
|
|
|
|
// writeHashTable writes the hash table to the file with the given data.
|
|
func (h *Hash) writeHashTable() error {
|
|
start := true
|
|
var data []byte
|
|
for path, hash := range h.hashTable {
|
|
if start {
|
|
start = false
|
|
} else {
|
|
data = append(data, []byte("\n")...)
|
|
}
|
|
|
|
data = append(data, []byte(fmt.Sprintf("%s %s", path, hash))...)
|
|
}
|
|
|
|
return os.WriteFile(h.hashFile, data, 0644)
|
|
}
|