2 syncer -- stateful file/device data syncer.
3 Copyright (C) 2015-2018 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // Stateful file/device data syncer.
32 "github.com/dchest/blake2b"
36 blkSize = flag.Int64("blk", 2*1<<10, "Block size (KiB)")
37 statePath = flag.String("state", "state.bin", "Path to statefile")
38 dstPath = flag.String("dst", "", "Path to destination disk")
39 srcPath = flag.String("src", "/dev/da0", "Path to source disk")
42 type SyncEvent struct {
50 bs := *blkSize * int64(1<<10)
53 log.Fatalln("Not destination is specified")
56 // Open source, calculate number of blocks
58 src, err := os.Open(*srcPath)
60 log.Fatalln("Unable to open src:", err)
65 log.Fatalln("Unable to read src stat:", err)
67 if fi.Mode()&os.ModeDevice == os.ModeDevice {
68 size, err = src.Seek(0, 2)
70 log.Fatalln("Unable to seek src:", err)
80 log.Println(blocks, bs, "byte blocks")
83 dst, err := os.OpenFile(*dstPath, os.O_WRONLY|os.O_CREATE, 0600)
85 log.Fatalln("Unable to open dst:", err)
89 // Check if we already have statefile and read the state
90 state := make([]byte, blake2b.Size*blocks)
93 if _, err := os.Stat(*statePath); err == nil {
94 log.Println("State file found")
95 stateFile, err := os.Open(*statePath)
97 log.Fatalln("Unable to read statefile:", err)
100 // Check previously used size and block size
101 tmp = make([]byte, 8)
102 n, err := stateFile.Read(tmp)
103 if err != nil || n != 8 {
104 log.Fatalln("Invalid statefile")
106 prevSize := int64(binary.BigEndian.Uint64(tmp))
107 if size != prevSize {
109 "Size differs with state file:",
110 prevSize, "instead of", size,
113 tmp = make([]byte, 8)
114 n, err = stateFile.Read(tmp)
115 if err != nil || n != 8 {
116 log.Fatalln("Invalid statefile")
118 prevBs := int64(binary.BigEndian.Uint64(tmp))
121 "Blocksize differs with state file:",
122 prevBs, "instead of", bs,
126 n, err = stateFile.Read(state)
127 if err != nil || n != len(state) {
128 log.Fatalln("Corrupted statefile")
132 stateFile, err := ioutil.TempFile(".", "syncer")
134 log.Fatalln("Unable to create temporary file:", err)
136 tmp = make([]byte, 8)
137 binary.BigEndian.PutUint64(tmp, uint64(size))
139 tmp = make([]byte, 8)
140 binary.BigEndian.PutUint64(tmp, uint64(bs))
143 // Create buffers and event channel
144 workers := runtime.NumCPU()
145 log.Println(workers, "workers")
146 bufs := make(chan []byte, workers)
147 for i := 0; i < workers; i++ {
148 bufs <- make([]byte, int(bs))
150 syncs := make(chan chan SyncEvent, workers)
153 prn := NewPrinter(blocks)
154 finished := make(chan struct{})
157 for sync := range syncs {
159 if event.data != nil {
160 dst.Seek(event.i*bs, 0)
161 dst.Write(event.data)
170 for i = 0; i < blocks; i++ {
172 n, err := src.Read(buf)
175 log.Fatalln("Error during src read:", err)
179 sync := make(chan SyncEvent)
182 sum := blake2b.Sum512(buf[:n])
183 sumState := state[i*blake2b.Size : i*blake2b.Size+blake2b.Size]
184 if bytes.Compare(sumState, sum[:]) != 0 {
185 sync <- SyncEvent{i, buf, buf[:n]}
188 sync <- SyncEvent{i, buf, nil}
191 copy(sumState, sum[:])
199 log.Println("Saving state")
200 stateFile.Write(state)
202 if err = os.Rename(stateFile.Name(), *statePath); err != nil {
204 "Unable to overwrite statefile:", err,
205 "saved state is in:", stateFile.Name(),