mirror of
https://github.com/vbatts/dedupe-linker.git
synced 2025-02-06 12:23:34 +00:00
base: actually do the hardlink of dupes
Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
parent
71720092db
commit
d820978518
3 changed files with 69 additions and 3 deletions
48
base/base.go
48
base/base.go
|
@ -2,6 +2,7 @@ package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -125,6 +126,49 @@ func (b Base) LinkFrom(src, sum string) error {
|
||||||
return os.Link(src, b.blobPath(sum))
|
return os.Link(src, b.blobPath(sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randomString() (string, error) {
|
||||||
|
// make a random name
|
||||||
|
buf := make([]byte, 10)
|
||||||
|
if _, err := rand.Read(buf); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%x", buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeLink overrides newname if it already exists. If there is an error in creating the link, the transaction is rolled back
|
||||||
|
func SafeLink(oldname, newname string) error {
|
||||||
|
var backupName string
|
||||||
|
// check if newname exists
|
||||||
|
if fi, err := os.Stat(newname); err == nil && fi != nil {
|
||||||
|
// make a random name
|
||||||
|
buf := make([]byte, 5)
|
||||||
|
if _, err = rand.Read(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
backupName = fmt.Sprintf("%s.%x", newname, buf)
|
||||||
|
// move newname to the random name backupName
|
||||||
|
if err = os.Rename(newname, backupName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// hardlink oldname to newname
|
||||||
|
if err := os.Link(oldname, newname); err != nil {
|
||||||
|
// if that failed, and there is a backupName
|
||||||
|
if len(backupName) > 0 {
|
||||||
|
// then move back the backup
|
||||||
|
if err = os.Rename(backupName, newname); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// remove the backupName
|
||||||
|
if len(backupName) > 0 {
|
||||||
|
os.Remove(backupName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Hard link the file for sum to the path at dest
|
// Hard link the file for sum to the path at dest
|
||||||
func (b Base) LinkTo(dest, sum string) error {
|
func (b Base) LinkTo(dest, sum string) error {
|
||||||
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil && !os.IsExist(err) {
|
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil && !os.IsExist(err) {
|
||||||
|
@ -133,8 +177,8 @@ func (b Base) LinkTo(dest, sum string) error {
|
||||||
err := os.Link(b.blobPath(sum), dest)
|
err := os.Link(b.blobPath(sum), dest)
|
||||||
if err != nil && os.IsExist(err) {
|
if err != nil && os.IsExist(err) {
|
||||||
if !b.SameFile(sum, dest) {
|
if !b.SameFile(sum, dest) {
|
||||||
// XXX
|
SafeLink(b.blobPath(sum), dest)
|
||||||
log.Printf("Would clobber %q with %q", dest, b.blobPath(sum))
|
log.Printf("dedupped %q with %q", dest, b.blobPath(sum))
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -17,6 +17,20 @@ func TestSumPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRand(t *testing.T) {
|
||||||
|
randmap := map[string]bool{}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
r, err := randomString()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, ok := randmap[r]; ok {
|
||||||
|
t.Errorf("expected no duplicates, but %q is a dup random string", r)
|
||||||
|
}
|
||||||
|
randmap[r] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetPut(t *testing.T) {
|
func TestGetPut(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
srcDir, destDir string
|
srcDir, destDir string
|
||||||
|
|
10
main.go
10
main.go
|
@ -18,6 +18,7 @@ var (
|
||||||
flCipher = flag.String("c", "sha1", "block cipher to use (sha1, or sha256)")
|
flCipher = flag.String("c", "sha1", "block cipher to use (sha1, or sha256)")
|
||||||
flWorkers = flag.Int("w", 2, "workers to do summing")
|
flWorkers = flag.Int("w", 2, "workers to do summing")
|
||||||
flNoop = flag.Bool("noop", false, "don't do any moving or linking")
|
flNoop = flag.Bool("noop", false, "don't do any moving or linking")
|
||||||
|
flDebug = flag.Bool("debug", false, "enable debug output")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -30,6 +31,10 @@ func init() {
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if *flDebug {
|
||||||
|
os.Setenv("DEBUG", "1")
|
||||||
|
}
|
||||||
|
|
||||||
// TODO the *flCipher has not been checked yet, and would cause the directory to get created
|
// TODO the *flCipher has not been checked yet, and would cause the directory to get created
|
||||||
ourbase, err := base.NewBase(*flVarBase, *flCipher)
|
ourbase, err := base.NewBase(*flVarBase, *flCipher)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,11 +66,14 @@ func main() {
|
||||||
if *flNoop {
|
if *flNoop {
|
||||||
fmt.Printf("%s [%d] %s\n", fi.Hash, fi.Size, fi.Path)
|
fmt.Printf("%s [%d] %s\n", fi.Hash, fi.Size, fi.Path)
|
||||||
} else {
|
} else {
|
||||||
|
if os.Getenv("DEBUG") != "" {
|
||||||
|
fmt.Printf("%q: %q\n", fi.Path, ourbase.HasBlob(fi.Hash))
|
||||||
|
}
|
||||||
if ourbase.HasBlob(fi.Hash) && !ourbase.SameFile(fi.Hash, fi.Path) {
|
if ourbase.HasBlob(fi.Hash) && !ourbase.SameFile(fi.Hash, fi.Path) {
|
||||||
if err := ourbase.LinkTo(fi.Path, fi.Hash); err != nil {
|
if err := ourbase.LinkTo(fi.Path, fi.Hash); err != nil {
|
||||||
log.Println("ERROR-1", err)
|
log.Println("ERROR-1", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else if !ourbase.HasBlob(fi.Hash) {
|
||||||
if err := ourbase.LinkFrom(fi.Path, fi.Hash); err != nil {
|
if err := ourbase.LinkFrom(fi.Path, fi.Hash); err != nil {
|
||||||
log.Println("ERROR-2", err)
|
log.Println("ERROR-2", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue