summaryrefslogtreecommitdiff
path: root/src/archive/tar/sparse_unix.go
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2017-09-01 02:57:46 -0700
committerJoe Tsai <thebrokentoaster@gmail.com>2017-09-20 22:12:38 +0000
commit1eacf78858fd18b100d25f7a04c4c62d96a23020 (patch)
tree88d73ddae40820cc66aa7849191ade5914f6ae34 /src/archive/tar/sparse_unix.go
parentd2f317218bf563121e80b36bb06a8ba46d040a20 (diff)
downloadgo-git-1eacf78858fd18b100d25f7a04c4c62d96a23020.tar.gz
archive/tar: add Header.DetectSparseHoles and Header.PunchSparseHoles
To support the detection and creation of sparse files, add two new methods: func Header.DetectSparseHoles(*os.File) error func Header.PunchSparseHoles(*os.File) error DetectSparseHoles is intended to be used after FileInfoHeader prior to serializing the Header with WriteHeader. For each OS, it uses specialized logic to detect the location of sparse holes. On most Unix systems, it uses SEEK_HOLE and SEEK_DATA to query for the holes. On Windows, it uses a specialized the FSCTL_QUERY_ALLOCATED_RANGES syscall to query for all the holes. PunchSparseHoles is intended to be used after Reader.Next prior to populating the file with Reader.WriteTo. On Windows, this uses the FSCTL_SET_ZERO_DATA syscall. On other operating systems it simply truncates the file to the end-offset of SparseHoles. DetectSparseHoles and PunchSparseHoles are added as methods on Header because they are heavily tied to the operating system, for which there is already an existing precedence for (since FileInfoHeader makes uses of OS-specific details). Fixes #13548 Change-Id: I98a321dd1ce0165f3d143d4edadfda5e7db67746 Reviewed-on: https://go-review.googlesource.com/60871 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
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
+}