// use blake do strong sum // sumLen: 32 or 64 // 2015-10-04: just use 64-byte hash func strongSum(p []byte, sumLen uint32) (s []byte) { /* if sumLen == 32 { sum := blake2b.Sum256(p) s = sum[0:sumLen] return } sumLen = 64 // make sure sumLen is 64 */ sum := blake2b.Sum512(p) s = sum[0:sumLen] return }
func main() { flag.Parse() bs := *blkSize * int64(1<<10) if *dstPath == "" { log.Fatalln("Not destination is specified") } // Open source, calculate number of blocks var size int64 src, err := os.Open(*srcPath) if err != nil { log.Fatalln("Unable to open src:", err) } defer src.Close() fi, err := src.Stat() if err != nil { log.Fatalln("Unable to read src stat:", err) } if fi.Mode()&os.ModeDevice == os.ModeDevice { size, err = src.Seek(0, 2) if err != nil { log.Fatalln("Unable to seek src:", err) } src.Seek(0, 0) } else { size = fi.Size() } blocks := size / bs if size%bs != 0 { blocks++ } log.Println(blocks, bs, "byte blocks") // Open destination dst, err := os.OpenFile(*dstPath, os.O_WRONLY|os.O_CREATE, 0600) if err != nil { log.Fatalln("Unable to open dst:", err) } defer dst.Close() // Check if we already have statefile and read the state state := make([]byte, blake2b.Size*blocks) var i int64 var tmp []byte if _, err := os.Stat(*statePath); err == nil { log.Println("State file found") stateFile, err := os.Open(*statePath) if err != nil { log.Fatalln("Unable to read statefile:", err) } // Check previously used size and block size tmp = make([]byte, 8) n, err := stateFile.Read(tmp) if err != nil || n != 8 { log.Fatalln("Invalid statefile") } prevSize := int64(binary.BigEndian.Uint64(tmp)) if size != prevSize { log.Fatalln( "Size differs with state file:", prevSize, "instead of", size, ) } tmp = make([]byte, 8) n, err = stateFile.Read(tmp) if err != nil || n != 8 { log.Fatalln("Invalid statefile") } prevBs := int64(binary.BigEndian.Uint64(tmp)) if bs != prevBs { log.Fatalln( "Blocksize differs with state file:", prevBs, "instead of", bs, ) } n, err = stateFile.Read(state) if err != nil || n != len(state) { log.Fatalln("Corrupted statefile") } stateFile.Close() } stateFile, err := ioutil.TempFile(".", "syncer") if err != nil { log.Fatalln("Unable to create temporary file:", err) } tmp = make([]byte, 8) binary.BigEndian.PutUint64(tmp, uint64(size)) stateFile.Write(tmp) tmp = make([]byte, 8) binary.BigEndian.PutUint64(tmp, uint64(bs)) stateFile.Write(tmp) // Create buffers and event channel workers := runtime.NumCPU() log.Println(workers, "workers") bufs := make(chan []byte, workers) for i := 0; i < workers; i++ { bufs <- make([]byte, int(bs)) } syncs := make(chan chan SyncEvent, workers) // Writer prn("[") finished := make(chan struct{}) go func() { var event SyncEvent for sync := range syncs { event = <-sync if event.data != nil { dst.Seek(event.i*bs, 0) dst.Write(event.data) } bufs <- event.buf <-sync } close(finished) }() // Reader for i = 0; i < blocks; i++ { buf := <-bufs n, err := src.Read(buf) if err != nil { if err != io.EOF { log.Fatalln("Error during src read:", err) } break } sync := make(chan SyncEvent) syncs <- sync go func(i int64) { sum := blake2b.Sum512(buf[:n]) sumState := state[i*blake2b.Size : i*blake2b.Size+blake2b.Size] if bytes.Compare(sumState, sum[:]) != 0 { sync <- SyncEvent{i, buf, buf[:n]} prn("%") } else { sync <- SyncEvent{i, buf, nil} prn(".") } copy(sumState, sum[:]) close(sync) }(i) } close(syncs) <-finished prn("]\n") log.Println("Saving state") stateFile.Write(state) stateFile.Close() if err = os.Rename(stateFile.Name(), *statePath); err != nil { log.Fatalln( "Unable to overwrite statefile:", err, "saved state is in:", stateFile.Name(), ) } }