Files
kopia/internal/sparsefile/sparsefile_test.go
Ali Dowair aafe56cd6f feat(snapshots): support restoring sparse files (#1823)
* feat(snapshots): support restoring sparse files

This commit implements basic support for restoring sparse files from
a snapshot. When specifying "--mode=sparse" in a snapshot restore
command, Kopia will make a best effort to make sure the underlying
filesystem allocates the minimum amount of blocks needed to persist
restored files. In other words, enabling this feature will "force"
all restored files to be sparse-blocks of zero bytes in the source
file should not be allocated.

* Address review comments

- Separate sparse option into its own bool flag
- Implement sparsefile packagewith copySparse method
- Truncate once before writing sparse file
- Check error from Truncate
- Add unit test for copySparse
- Invoke GetBlockSize once per file copy
- Remove support for Windows and explain why
- Add unit test for stat package

Co-authored-by: Dave Smith-Uchida <dave@kasten.io>
2022-03-22 19:09:50 -07:00

99 lines
1.5 KiB
Go

package sparsefile
import (
"bytes"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/kopia/kopia/internal/stat"
)
func TestSparseWrite(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
t.Skip("sparse files are not supported on windows")
}
dir := t.TempDir()
blk, err := stat.GetBlockSize(dir)
if err != nil {
t.Fatal(err)
}
type chunk struct {
slice []byte
off uint64
rep uint64
}
cases := []struct {
name string
size uint64
data []chunk
}{
{
name: "null",
size: 0,
},
{
name: "empty",
size: blk,
data: []chunk{
{slice: []byte{0}, off: 0, rep: blk},
},
},
{
name: "hole",
size: 2 * blk,
data: []chunk{
{slice: []byte{1}, off: blk, rep: blk},
},
},
{
name: "mix",
size: 2 * blk,
data: []chunk{
{slice: []byte{1}, off: 3, rep: blk - 10},
{slice: []byte{1}, off: 2*blk - 10, rep: 10},
},
},
}
for _, c := range cases {
src := filepath.Join(dir, "src"+c.name)
dst := filepath.Join(dir, "dst"+c.name)
fd, err := os.Create(src)
if err != nil {
t.Fatal(err)
}
for _, d := range c.data {
fd.WriteAt(bytes.Repeat(d.slice, int(d.rep)), int64(d.off))
}
err = Write(dst, fd, int64(c.size))
if err != nil {
t.Fatalf("error writing %s: %v", dst, err)
}
s, err := os.ReadFile(src)
if err != nil {
t.Fatal(err)
}
d, err := os.ReadFile(dst)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(s, d) {
t.Fatalf("contents of %s and %s are not identical", src, dst)
}
}
}