Skip to content

Commit 60e87ce

Browse files
committed
Ignore and fix compression format errors in target. Performance improvements and code cleanup.
1 parent 9339e2d commit 60e87ce

File tree

6 files changed

+850
-125
lines changed

6 files changed

+850
-125
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
on:
22
release:
33
types: [created]
4-
4+
name: Release
55
jobs:
66
releases-matrix:
77
name: Release Go Binary

diskrsync/main.go

Lines changed: 93 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,8 @@ type remoteProc struct {
3939
cmd *exec.Cmd
4040
}
4141

42-
type targetFile interface {
43-
io.ReadWriteSeeker
44-
io.Closer
45-
}
46-
4742
func usage() {
48-
fmt.Fprintf(os.Stderr, "Usage: %s [--ssh-flags=\"...\"] [--no-compress] [--verbose] <src> <dst>\nsrc and dst is [[user@]host:]path\n", os.Args[0])
43+
_, _ = fmt.Fprintf(os.Stderr, "Usage: %s [--ssh-flags=\"...\"] [--no-compress] [--verbose] <src> <dst>\nsrc and dst is [[user@]host:]path\n", os.Args[0])
4944
os.Exit(2)
5045
}
5146

@@ -118,41 +113,51 @@ func (p *localProc) Start(cmdReader io.Reader, cmdWriter io.WriteCloser, errChan
118113
}
119114

120115
func (p *localProc) run(cmdReader io.Reader, cmdWriter io.WriteCloser, errChan chan error) {
116+
var err error
121117
if p.mode == modeSource {
122-
errChan <- doSource(p.p, cmdReader, cmdWriter, p.opts)
118+
err = doSource(p.p, cmdReader, cmdWriter, p.opts)
123119
} else {
124-
errChan <- doTarget(p.p, cmdReader, cmdWriter, p.opts)
120+
err = doTarget(p.p, cmdReader, cmdWriter, p.opts)
121+
}
122+
123+
cerr := cmdWriter.Close()
124+
if err == nil {
125+
err = cerr
125126
}
127+
errChan <- err
128+
}
126129

127-
cmdWriter.Close()
130+
func (p *remoteProc) pipeCopy(dst io.WriteCloser, src io.Reader) {
131+
_, err := io.Copy(dst, src)
132+
if err != nil {
133+
log.Printf("pipe copy failed: %v", err)
134+
}
135+
err = dst.Close()
136+
if err != nil {
137+
log.Printf("close failed after pipe copy: %v", err)
138+
}
128139
}
129140

130141
func (p *remoteProc) Start(cmdReader io.Reader, cmdWriter io.WriteCloser, errChan chan error) error {
131-
p.cmd.Stdout = cmdWriter
132142
p.cmd.Stderr = os.Stderr
143+
p.cmd.Stdin = cmdReader
133144

134-
w, err := p.cmd.StdinPipe()
145+
r, err := p.cmd.StdoutPipe()
135146
if err != nil {
136147
return err
137148
}
138149

139-
go func() {
140-
io.Copy(w, cmdReader)
141-
w.Close()
142-
}()
143-
144150
err = p.cmd.Start()
145151
if err != nil {
146152
return err
147153
}
148-
go p.run(cmdWriter, errChan)
154+
go p.run(cmdWriter, r, errChan)
149155
return nil
150156
}
151157

152-
func (p *remoteProc) run(writer io.Closer, errChan chan error) {
153-
err := p.cmd.Wait()
154-
writer.Close()
155-
errChan <- err
158+
func (p *remoteProc) run(w io.WriteCloser, r io.Reader, errChan chan error) {
159+
p.pipeCopy(w, r)
160+
errChan <- p.cmd.Wait()
156161
}
157162

158163
func doSource(p string, cmdReader io.Reader, cmdWriter io.WriteCloser, opts *options) error {
@@ -176,83 +181,93 @@ func doSource(p string, cmdReader io.Reader, cmdWriter io.WriteCloser, opts *opt
176181
src = sf
177182
}
178183

179-
size, err := src.Seek(0, os.SEEK_END)
184+
size, err := src.Seek(0, io.SeekEnd)
180185
if err != nil {
181186
return err
182187
}
183188

184-
_, err = src.Seek(0, os.SEEK_SET)
189+
_, err = src.Seek(0, io.SeekStart)
185190
if err != nil {
186191
return err
187192
}
188193

189194
err = diskrsync.Source(src, size, cmdReader, cmdWriter, true, opts.verbose)
190-
cmdWriter.Close()
195+
cerr := cmdWriter.Close()
196+
if err == nil {
197+
err = cerr
198+
}
191199
return err
192200
}
193201

194-
func doTarget(p string, cmdReader io.Reader, cmdWriter io.WriteCloser, opts *options) error {
195-
var w targetFile
196-
useBuffer := false
202+
func doTarget(p string, cmdReader io.Reader, cmdWriter io.WriteCloser, opts *options) (err error) {
203+
var w spgz.SparseFile
204+
useReadBuffer := false
197205

198206
f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE, 0666)
199207
if err != nil {
200-
return err
208+
return
201209
}
202210

203211
info, err := f.Stat()
204212
if err != nil {
205-
f.Close()
206-
return err
213+
_ = f.Close()
214+
return
207215
}
208216

209-
if info.Mode() & (os.ModeDevice | os.ModeCharDevice) != 0 {
210-
w = f
211-
useBuffer = true
217+
if info.Mode()&(os.ModeDevice|os.ModeCharDevice) != 0 {
218+
w = spgz.NewSparseFileWithoutHolePunching(f)
219+
useReadBuffer = true
212220
} else if !opts.noCompress {
213221
sf, err := spgz.NewFromFileSize(f, os.O_RDWR|os.O_CREATE, diskrsync.DefTargetBlockSize)
214222
if err != nil {
215223
if err != spgz.ErrInvalidFormat {
216224
if err == spgz.ErrPunchHoleNotSupported {
217225
err = fmt.Errorf("target does not support compression. Try with -no-compress option (error was '%v')", err)
218226
}
219-
f.Close()
227+
_ = f.Close()
220228
return err
221229
}
222230
} else {
223-
w = sf
231+
w = &diskrsync.FixingSpgzFileWrapper{SpgzFile: sf}
224232
}
225233
}
226234

227235
if w == nil {
228-
w = spgz.NewSparseWriter(spgz.NewSparseFileWithFallback(f))
229-
useBuffer = true
236+
w = spgz.NewSparseFileWithFallback(f)
237+
useReadBuffer = true
230238
}
231239

232-
defer w.Close()
240+
defer func() {
241+
cerr := w.Close()
242+
if err == nil {
243+
err = cerr
244+
}
245+
}()
233246

234-
size, err := w.Seek(0, os.SEEK_END)
247+
size, err := w.Seek(0, io.SeekEnd)
235248
if err != nil {
236249
return err
237250
}
238251

239-
_, err = w.Seek(0, os.SEEK_SET)
252+
_, err = w.Seek(0, io.SeekStart)
240253

241254
if err != nil {
242255
return err
243256
}
244257

245-
err = diskrsync.Target(w, size, cmdReader, cmdWriter, useBuffer, opts.verbose)
246-
cmdWriter.Close()
258+
err = diskrsync.Target(w, size, cmdReader, cmdWriter, useReadBuffer, opts.verbose)
259+
cerr := cmdWriter.Close()
260+
if err == nil {
261+
err = cerr
262+
}
247263

248-
return err
264+
return
249265
}
250266

251-
func doCmd(opts *options) bool {
267+
func doCmd(opts *options) (err error) {
252268
src, err := createProc(flag.Arg(0), modeSource, opts)
253269
if err != nil {
254-
log.Printf("Could not create source: %v", err)
255-
return false
270+
return fmt.Errorf("could not create source: %w", err)
256271
}
257272

258273
path := flag.Arg(1)
@@ -262,8 +277,7 @@ func doCmd(opts *options) bool {
262277

263278
dst, err := createProc(path, modeTarget, opts)
264279
if err != nil {
265-
log.Printf("Could not create target: %v", err)
266-
return false
280+
return fmt.Errorf("could not create target: %w", err)
267281
}
268282

269283
srcErrChan := make(chan error, 1)
@@ -276,24 +290,42 @@ func doCmd(opts *options) bool {
276290
sw := &diskrsync.CountingWriteCloser{WriteCloser: srcWriter}
277291

278292
if opts.verbose {
279-
src.Start(sr, sw, srcErrChan)
293+
err = src.Start(sr, sw, srcErrChan)
280294
} else {
281-
src.Start(srcReader, srcWriter, srcErrChan)
295+
err = src.Start(srcReader, srcWriter, srcErrChan)
296+
}
297+
298+
if err != nil {
299+
return fmt.Errorf("could not start source: %w", err)
282300
}
283301

284-
dst.Start(dstReader, dstWriter, dstErrChan)
285-
dstErr := <-dstErrChan
286-
if dstErr != nil {
287-
log.Printf("Target error: %v", dstErr)
302+
err = dst.Start(dstReader, dstWriter, dstErrChan)
303+
if err != nil {
304+
return fmt.Errorf("could not start target: %w", err)
288305
}
289-
srcErr := <-srcErrChan
290-
if srcErr != nil {
291-
log.Printf("Source error: %v", srcErr)
306+
307+
L:
308+
for srcErrChan != nil || dstErrChan != nil {
309+
select {
310+
case dstErr := <-dstErrChan:
311+
if dstErr != nil {
312+
err = fmt.Errorf("target error: %w", dstErr)
313+
break L
314+
}
315+
dstErrChan = nil
316+
case srcErr := <-srcErrChan:
317+
if srcErr != nil {
318+
err = fmt.Errorf("source error: %w", srcErr)
319+
break L
320+
}
321+
srcErrChan = nil
322+
}
292323
}
324+
293325
if opts.verbose {
294326
log.Printf("Read: %d, wrote: %d\n", sr.Count(), sw.Count())
295327
}
296-
return srcErr == nil && dstErr == nil
328+
return
297329
}
298330

299331
func main() {
@@ -328,9 +360,9 @@ func main() {
328360
if flag.Arg(0) == "" || flag.Arg(1) == "" {
329361
usage()
330362
}
331-
ok := doCmd(&opts)
332-
if !ok {
333-
os.Exit(1)
363+
err := doCmd(&opts)
364+
if err != nil {
365+
log.Fatal(err)
334366
}
335367
}
336368

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ module github.com/dop251/diskrsync
33
go 1.16
44

55
require (
6-
github.com/dop251/spgz v0.0.0-20180204132655-b86304a2b188
6+
github.com/dop251/spgz v1.1.0
77
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
88
)

go.sum

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
github.com/dop251/spgz v0.0.0-20180204132655-b86304a2b188 h1:UYxfuh/hzW7AmbV9eAhr3mh8Qjr34z4fD5PlFbBaiUI=
2-
github.com/dop251/spgz v0.0.0-20180204132655-b86304a2b188/go.mod h1:LNJUPCpuM80Fs0wTQ3+0oRp6h26KO5mAv4jOTLShJ8w=
1+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/dop251/buse v0.0.0-20170916130217-f7a5c857babd/go.mod h1:hQb8UeARubyuKpfzN+fkxK9TTytxdWRCalLjf/FQgwk=
3+
github.com/dop251/nbd v0.0.0-20170916130042-b8933b281cb7/go.mod h1:/YqO/I24sucjxhCgQHgDrnffSwg5HzoYHQASayZnYl8=
4+
github.com/dop251/spgz v1.1.0 h1:y49BXvoyhF+Y9No69DCJLqTCACleK27B73XWsXa2nFU=
5+
github.com/dop251/spgz v1.1.0/go.mod h1:aXXbApWJzaK6jzPiIWWLFi3k47VmRFfunUp2ANdQFD8=
6+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
7+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8+
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
9+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
310
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
411
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
512
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
13+
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
614
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
715
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
816
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

0 commit comments

Comments
 (0)