mirror of
https://github.com/vbatts/dedupe-linker.git
synced 2024-11-16 05:38:38 +00:00
150 lines
3.3 KiB
Go
150 lines
3.3 KiB
Go
package base
|
|
|
|
import (
|
|
"crypto"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/vbatts/dedupe-linker/cryptomap"
|
|
"github.com/vbatts/dedupe-linker/file"
|
|
)
|
|
|
|
func NewBase(path string, hashName string) (*Base, error) {
|
|
root := filepath.Join(path, "dedup")
|
|
for _, p := range []string{"blobs/" + hashName, "state", "tmp"} {
|
|
if err := os.MkdirAll(filepath.Join(root, p), 0755); err != nil && !os.IsExist(err) {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &Base{Path: root, HashName: hashName, Hash: cryptomap.DetermineHash(hashName)}, nil
|
|
}
|
|
|
|
type Base struct {
|
|
Path string
|
|
HashName string
|
|
Hash crypto.Hash
|
|
}
|
|
|
|
func (b Base) Stat(sum string) (os.FileInfo, error) {
|
|
return os.Stat(b.blobPath(sum))
|
|
}
|
|
|
|
func (b Base) blobPath(sum string) string {
|
|
if len(sum) < 3 {
|
|
return ""
|
|
}
|
|
return filepath.Join(b.Path, "blobs", b.HashName, sum[0:2], sum)
|
|
}
|
|
|
|
type ReaderSeekerCloser interface {
|
|
io.Reader
|
|
io.Seeker
|
|
io.Closer
|
|
}
|
|
|
|
func (b Base) SameFile(sum, path string) bool {
|
|
var (
|
|
bInode, dInode uint64
|
|
err error
|
|
)
|
|
if bInode, err = file.GetInode(b.blobPath(sum)); err != nil {
|
|
return false
|
|
}
|
|
if dInode, err = file.GetInode(path); err != nil {
|
|
return false
|
|
}
|
|
if bInode == dInode {
|
|
return true
|
|
}
|
|
return false
|
|
|
|
}
|
|
|
|
// GetBlob store the content from src, for the sum and hashType
|
|
func (b Base) GetBlob(sum string) (ReaderSeekerCloser, error) {
|
|
return os.Open(b.blobPath(sum))
|
|
}
|
|
|
|
// PutBlob store the content from src, for the sum and hashType
|
|
//
|
|
// we take the sum up front to avoid recalculation and tempfiles
|
|
func (b Base) PutBlob(src io.Reader, mode os.FileMode) (string, error) {
|
|
fh, err := b.tmpFile()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer func() {
|
|
fh.Close()
|
|
os.Remove(fh.Name())
|
|
}()
|
|
|
|
h := b.Hash.New()
|
|
t := io.TeeReader(src, h)
|
|
|
|
if _, err = io.Copy(fh, t); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
sum := fmt.Sprintf("%x", h.Sum(nil))
|
|
fi, err := b.Stat(sum)
|
|
if err == nil && fi.Mode().IsRegular() {
|
|
return sum, nil
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(b.blobPath(sum)), 0755); err != nil && !os.IsExist(err) {
|
|
return sum, err
|
|
}
|
|
destFh, err := os.Create(b.blobPath(sum))
|
|
if err != nil {
|
|
return sum, err
|
|
}
|
|
defer destFh.Close()
|
|
_, err = fh.Seek(0, 0)
|
|
if err != nil {
|
|
return sum, err
|
|
}
|
|
if _, err = io.Copy(destFh, fh); err != nil {
|
|
return sum, err
|
|
}
|
|
return sum, destFh.Chmod(mode)
|
|
}
|
|
|
|
func (b Base) tmpFile() (*os.File, error) {
|
|
return ioutil.TempFile(filepath.Join(b.Path, "tmp"), "put")
|
|
}
|
|
|
|
// Hard link the file from src to the blob for sum
|
|
func (b Base) LinkFrom(src, sum string) error {
|
|
if err := os.MkdirAll(filepath.Dir(b.blobPath(sum)), 0756); err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
return os.Link(src, b.blobPath(sum))
|
|
}
|
|
|
|
// Hard link the file for sum to the path at dest
|
|
func (b Base) LinkTo(dest, sum string) error {
|
|
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
err := os.Link(b.blobPath(sum), dest)
|
|
if err != nil && os.IsExist(err) {
|
|
if !b.SameFile(sum, dest) {
|
|
// XXX
|
|
log.Printf("Would clobber %q with %q", dest, b.blobPath(sum))
|
|
}
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// HasBlob tests whether a blob with this sum exists
|
|
func (b Base) HasBlob(sum string) bool {
|
|
fi, err := b.Stat(sum)
|
|
log.Println("SUCH FARTS", fi)
|
|
return fi != nil && err == nil
|
|
}
|