forked from TrueCloudLab/restic
a5ebd5de4b
The restorer can issue multiple calls to WriteAt in parallel. This can result in unexpected orderings of the Truncate and WriteAt calls and sometimes too short restored files.
53 lines
1.1 KiB
Go
53 lines
1.1 KiB
Go
//go:build !windows
|
|
// +build !windows
|
|
|
|
package restorer
|
|
|
|
import "bytes"
|
|
|
|
// WriteAt writes p to f.File at offset. It tries to do a sparse write
|
|
// and updates f.size.
|
|
func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
|
if !f.sparse {
|
|
return f.File.WriteAt(p, offset)
|
|
}
|
|
|
|
n = len(p)
|
|
|
|
// Skip the longest all-zero prefix of p.
|
|
// If it's long enough, we can punch a hole in the file.
|
|
skipped := zeroPrefixLen(p)
|
|
p = p[skipped:]
|
|
offset += int64(skipped)
|
|
|
|
switch {
|
|
case len(p) == 0:
|
|
// All zeros, file already big enough. A previous WriteAt or
|
|
// Truncate will have produced the zeros in f.File.
|
|
|
|
default:
|
|
var n2 int
|
|
n2, err = f.File.WriteAt(p, offset)
|
|
n = skipped + n2
|
|
}
|
|
|
|
return n, err
|
|
}
|
|
|
|
// zeroPrefixLen returns the length of the longest all-zero prefix of p.
|
|
func zeroPrefixLen(p []byte) (n int) {
|
|
// First skip 1kB-sized blocks, for speed.
|
|
var zeros [1024]byte
|
|
|
|
for len(p) >= len(zeros) && bytes.Equal(p[:len(zeros)], zeros[:]) {
|
|
p = p[len(zeros):]
|
|
n += len(zeros)
|
|
}
|
|
|
|
for len(p) > 0 && p[0] == 0 {
|
|
p = p[1:]
|
|
n++
|
|
}
|
|
|
|
return n
|
|
}
|