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 }