// 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" "runtime" "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. var seekData, seekHole = 3, 4 // SEEK_DATA/SEEK_HOLE from unistd.h if runtime.GOOS == "darwin" { // Darwin has the constants swapped, compared to all other UNIX. seekData, seekHole = 4, 3 } // Check for seekData/seekHole support. // Different OS and FS may differ in the exact errno that is returned when // there is no support. Rather than special-casing every possible errno // representing "not supported", just assume that a non-nil error means // that seekData/seekHole is not supported. if _, err := f.Seek(0, seekHole); err != nil { return nil, nil } // 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 }