summaryrefslogtreecommitdiff
path: root/src/archive/tar/sparse_unix.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/archive/tar/sparse_unix.go')
-rw-r--r--src/archive/tar/sparse_unix.go68
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
+}