func main() { log.SetFlags(0) log.SetOutput(os.Stdout) standardBlocks := flag.Bool("s", false, "Use standard block size") flag.Parse() path := flag.Arg(0) if path == "" { log.Fatal("Need one argument: path to check") } log.Println("File:") log.Println(" ", filepath.Clean(path)) log.Println() fi, err := os.Lstat(path) if err != nil { log.Fatal(err) } log.Println("Lstat:") log.Printf(" Size: %d bytes", fi.Size()) log.Printf(" Mode: 0%o", fi.Mode()) log.Printf(" Time: %v (%d)", fi.ModTime(), fi.ModTime().Unix()) log.Println() if !fi.Mode().IsDir() && !fi.Mode().IsRegular() { fi, err = os.Stat(path) if err != nil { log.Fatal(err) } log.Println("Stat:") log.Printf(" Size: %d bytes", fi.Size()) log.Printf(" Mode: 0%o", fi.Mode()) log.Printf(" Time: %v (%d)", fi.ModTime(), fi.ModTime().Unix()) log.Println() } if fi.Mode().IsRegular() { log.Println("Blocks:") fd, err := os.Open(path) if err != nil { log.Fatal(err) } blockSize := int(fi.Size()) if *standardBlocks || blockSize < protocol.BlockSize { blockSize = protocol.BlockSize } bs, err := scanner.Blocks(fd, blockSize, fi.Size(), nil) if err != nil { log.Fatal(err) } for _, b := range bs { log.Println(" ", b) } } }
func TestWeakHash(t *testing.T) { tempFile := filepath.Join("testdata", ignore.TempName("weakhash")) var shift int64 = 10 var size int64 = 1 << 20 expectBlocks := int(size / protocol.BlockSize) expectPulls := int(shift / protocol.BlockSize) if shift > 0 { expectPulls++ } cleanup := func() { for _, path := range []string{tempFile, "testdata/weakhash"} { os.Remove(path) } } cleanup() defer cleanup() f, err := os.Create("testdata/weakhash") if err != nil { t.Error(err) } defer f.Close() _, err = io.CopyN(f, rand.Reader, size) if err != nil { t.Error(err) } info, err := f.Stat() if err != nil { t.Error(err) } // Create two files, second file has `shifted` bytes random prefix, yet // both are of the same length, for example: // File 1: abcdefgh // File 2: xyabcdef f.Seek(0, os.SEEK_SET) existing, err := scanner.Blocks(f, protocol.BlockSize, size, nil) if err != nil { t.Error(err) } f.Seek(0, os.SEEK_SET) remainder := io.LimitReader(f, size-shift) prefix := io.LimitReader(rand.Reader, shift) nf := io.MultiReader(prefix, remainder) desired, err := scanner.Blocks(nf, protocol.BlockSize, size, nil) if err != nil { t.Error(err) } existingFile := protocol.FileInfo{ Name: "weakhash", Blocks: existing, Size: size, ModifiedS: info.ModTime().Unix(), ModifiedNs: int32(info.ModTime().Nanosecond()), } desiredFile := protocol.FileInfo{ Name: "weakhash", Size: size, Blocks: desired, ModifiedS: info.ModTime().Unix() + 1, } // Setup the model/pull environment m := setUpModel(existingFile) fo := setUpSendReceiveFolder(m) copyChan := make(chan copyBlocksState) pullChan := make(chan pullBlockState, expectBlocks) finisherChan := make(chan *sharedPullerState, 1) // Run a single fetcher routine go fo.copierRoutine(copyChan, pullChan, finisherChan) // Test 1 - no weak hashing, file gets fully repulled (`expectBlocks` pulls). fo.WeakHashThresholdPct = 101 fo.handleFile(desiredFile, copyChan, finisherChan) var pulls []pullBlockState for len(pulls) < expectBlocks { select { case pull := <-pullChan: pulls = append(pulls, pull) case <-time.After(10 * time.Second): t.Errorf("timed out, got %d pulls expected %d", len(pulls), expectPulls) } } finish := <-finisherChan select { case <-pullChan: t.Fatal("Pull channel has data to be read") case <-finisherChan: t.Fatal("Finisher channel has data to be read") default: } finish.fd.Close() if err := os.Remove(tempFile); err != nil && !os.IsNotExist(err) { t.Error(err) } // Test 2 - using weak hash, expectPulls blocks pulled. fo.WeakHashThresholdPct = -1 fo.handleFile(desiredFile, copyChan, finisherChan) pulls = pulls[:0] for len(pulls) < expectPulls { select { case pull := <-pullChan: pulls = append(pulls, pull) case <-time.After(10 * time.Second): t.Errorf("timed out, got %d pulls expected %d", len(pulls), expectPulls) } } finish = <-finisherChan finish.fd.Close() expectShifted := expectBlocks - expectPulls if finish.copyOriginShifted != expectShifted { t.Errorf("did not copy %d shifted", expectShifted) } }