restorer: Fix race condition in partialFile.WriteAt
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.
This commit is contained in:
parent
5b6a77058a
commit
a5ebd5de4b
2 changed files with 20 additions and 32 deletions
|
@ -24,8 +24,7 @@ type filesWriterBucket struct {
|
||||||
|
|
||||||
type partialFile struct {
|
type partialFile struct {
|
||||||
*os.File
|
*os.File
|
||||||
size int64 // File size, tracked for sparse writes (not on Windows).
|
users int // Reference count.
|
||||||
users int // Reference count.
|
|
||||||
sparse bool
|
sparse bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,24 +63,24 @@ func (w *filesWriter) writeToFile(path string, blob []byte, offset int64, create
|
||||||
}
|
}
|
||||||
|
|
||||||
wr := &partialFile{File: f, users: 1, sparse: sparse}
|
wr := &partialFile{File: f, users: 1, sparse: sparse}
|
||||||
if createSize < 0 {
|
|
||||||
info, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
wr.size = info.Size()
|
|
||||||
}
|
|
||||||
bucket.files[path] = wr
|
bucket.files[path] = wr
|
||||||
|
|
||||||
if createSize >= 0 && !sparse {
|
if createSize >= 0 {
|
||||||
err := preallocateFile(wr.File, createSize)
|
if sparse {
|
||||||
if err != nil {
|
err = f.Truncate(createSize)
|
||||||
// Just log the preallocate error but don't let it cause the restore process to fail.
|
if err != nil {
|
||||||
// Preallocate might return an error if the filesystem (implementation) does not
|
return nil, err
|
||||||
// support preallocation or our parameters combination to the preallocate call
|
}
|
||||||
// This should yield a syscall.ENOTSUP error, but some other errors might also
|
} else {
|
||||||
// show up.
|
err := preallocateFile(wr.File, createSize)
|
||||||
debug.Log("Failed to preallocate %v with size %v: %v", path, createSize, err)
|
if err != nil {
|
||||||
|
// Just log the preallocate error but don't let it cause the restore process to fail.
|
||||||
|
// Preallocate might return an error if the filesystem (implementation) does not
|
||||||
|
// support preallocation or our parameters combination to the preallocate call
|
||||||
|
// This should yield a syscall.ENOTSUP error, but some other errors might also
|
||||||
|
// show up.
|
||||||
|
debug.Log("Failed to preallocate %v with size %v: %v", path, createSize, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
n = len(p)
|
n = len(p)
|
||||||
end := offset + int64(n)
|
|
||||||
|
|
||||||
// Skip the longest all-zero prefix of p.
|
// Skip the longest all-zero prefix of p.
|
||||||
// If it's long enough, we can punch a hole in the file.
|
// If it's long enough, we can punch a hole in the file.
|
||||||
|
@ -22,26 +21,16 @@ func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
||||||
offset += int64(skipped)
|
offset += int64(skipped)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case len(p) == 0 && end > f.size:
|
|
||||||
// We need to do a Truncate, as WriteAt with length-0 input
|
|
||||||
// doesn't actually extend the file.
|
|
||||||
err = f.Truncate(end)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
case len(p) == 0:
|
case len(p) == 0:
|
||||||
// All zeros, file already big enough. A previous WriteAt or
|
// All zeros, file already big enough. A previous WriteAt or
|
||||||
// Truncate will have produced the zeros in f.File.
|
// Truncate will have produced the zeros in f.File.
|
||||||
|
|
||||||
default:
|
default:
|
||||||
n, err = f.File.WriteAt(p, offset)
|
var n2 int
|
||||||
|
n2, err = f.File.WriteAt(p, offset)
|
||||||
|
n = skipped + n2
|
||||||
}
|
}
|
||||||
|
|
||||||
end = offset + int64(n)
|
|
||||||
if end > f.size {
|
|
||||||
f.size = end
|
|
||||||
}
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue