diff options
Diffstat (limited to 'src/archive/tar/sparse_unix.go')
-rw-r--r-- | src/archive/tar/sparse_unix.go | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/src/archive/tar/sparse_unix.go b/src/archive/tar/sparse_unix.go new file mode 100644 index 0000000000..76b4c6cc2b --- /dev/null +++ b/src/archive/tar/sparse_unix.go @@ -0,0 +1,68 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin dragonfly freebsd openbsd netbsd solaris + +package tar + +import ( + "io" + "os" + "syscall" +) + +func init() { + sysSparseDetect = sparseDetectUnix +} + +func sparseDetectUnix(f *os.File) (sph sparseHoles, err error) { + // SEEK_DATA and SEEK_HOLE originated from Solaris and support for it + // has been added to most of the other major Unix systems. + const seekData = 3 // SEEK_DATA from unistd.h + const seekHole = 4 // SEEK_HOLE from unistd.h + + // Check for seekData/seekHole support. + if _, err := f.Seek(0, seekHole); errno(err) == syscall.EINVAL { + return nil, nil // Either old kernel or FS does not support this + } + + // Populate the SparseHoles. + var last, pos int64 = -1, 0 + for { + // Get the location of the next hole section. + if pos, err = fseek(f, pos, seekHole); pos == last || err != nil { + return sph, err + } + offset := pos + last = pos + + // Get the location of the next data section. + if pos, err = fseek(f, pos, seekData); pos == last || err != nil { + return sph, err + } + length := pos - offset + last = pos + + if length > 0 { + sph = append(sph, SparseEntry{offset, length}) + } + } +} + +func fseek(f *os.File, pos int64, whence int) (int64, error) { + pos, err := f.Seek(pos, whence) + if errno(err) == syscall.ENXIO { + // SEEK_DATA returns ENXIO when past the last data fragment, + // which makes determining the size of the last hole difficult. + pos, err = f.Seek(0, io.SeekEnd) + } + return pos, err +} + +func errno(err error) error { + if perr, ok := err.(*os.PathError); ok { + return perr.Err + } + return err +} |