78 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			78 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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
 | 
						|
}
 |