github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/archive/tar/sparse_unix.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build linux darwin dragonfly freebsd openbsd netbsd solaris
     6  
     7  package tar
     8  
     9  import (
    10  	"io"
    11  	"os"
    12  	"runtime"
    13  	"syscall"
    14  )
    15  
    16  func init() {
    17  	sysSparseDetect = sparseDetectUnix
    18  }
    19  
    20  func sparseDetectUnix(f *os.File) (sph sparseHoles, err error) {
    21  	// SEEK_DATA and SEEK_HOLE originated from Solaris and support for it
    22  	// has been added to most of the other major Unix systems.
    23  	var seekData, seekHole = 3, 4 // SEEK_DATA/SEEK_HOLE from unistd.h
    24  
    25  	if runtime.GOOS == "darwin" {
    26  		// Darwin has the constants swapped, compared to all other UNIX.
    27  		seekData, seekHole = 4, 3
    28  	}
    29  
    30  	// Check for seekData/seekHole support.
    31  	// Different OS and FS may differ in the exact errno that is returned when
    32  	// there is no support. Rather than special-casing every possible errno
    33  	// representing "not supported", just assume that a non-nil error means
    34  	// that seekData/seekHole is not supported.
    35  	if _, err := f.Seek(0, seekHole); err != nil {
    36  		return nil, nil
    37  	}
    38  
    39  	// Populate the SparseHoles.
    40  	var last, pos int64 = -1, 0
    41  	for {
    42  		// Get the location of the next hole section.
    43  		if pos, err = fseek(f, pos, seekHole); pos == last || err != nil {
    44  			return sph, err
    45  		}
    46  		offset := pos
    47  		last = pos
    48  
    49  		// Get the location of the next data section.
    50  		if pos, err = fseek(f, pos, seekData); pos == last || err != nil {
    51  			return sph, err
    52  		}
    53  		length := pos - offset
    54  		last = pos
    55  
    56  		if length > 0 {
    57  			sph = append(sph, SparseEntry{offset, length})
    58  		}
    59  	}
    60  }
    61  
    62  func fseek(f *os.File, pos int64, whence int) (int64, error) {
    63  	pos, err := f.Seek(pos, whence)
    64  	if errno(err) == syscall.ENXIO {
    65  		// SEEK_DATA returns ENXIO when past the last data fragment,
    66  		// which makes determining the size of the last hole difficult.
    67  		pos, err = f.Seek(0, io.SeekEnd)
    68  	}
    69  	return pos, err
    70  }
    71  
    72  func errno(err error) error {
    73  	if perr, ok := err.(*os.PathError); ok {
    74  		return perr.Err
    75  	}
    76  	return err
    77  }