github.com/hanwen/go-fuse@v1.0.0/splice/splice.go (about) 1 // Copyright 2016 the Go-FUSE 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 package splice 6 7 // Routines for efficient file to file copying. 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "log" 13 "os" 14 "syscall" 15 ) 16 17 var maxPipeSize int 18 var resizable bool 19 20 func Resizable() bool { 21 return resizable 22 } 23 24 func MaxPipeSize() int { 25 return maxPipeSize 26 } 27 28 // From manpage on ubuntu Lucid: 29 // 30 // Since Linux 2.6.11, the pipe capacity is 65536 bytes. 31 const DefaultPipeSize = 16 * 4096 32 33 // We empty pipes by splicing to /dev/null. 34 var devNullFD uintptr 35 36 func init() { 37 content, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size") 38 if err != nil { 39 maxPipeSize = DefaultPipeSize 40 } else { 41 fmt.Sscan(string(content), &maxPipeSize) 42 } 43 44 r, w, err := os.Pipe() 45 if err != nil { 46 log.Panicf("cannot create pipe: %v", err) 47 } 48 sz, errNo := fcntl(r.Fd(), F_GETPIPE_SZ, 0) 49 resizable = (errNo == 0) 50 _, errNo = fcntl(r.Fd(), F_SETPIPE_SZ, 2*sz) 51 resizable = resizable && (errNo == 0) 52 r.Close() 53 w.Close() 54 55 fd, err := syscall.Open("/dev/null", os.O_WRONLY, 0) 56 if err != nil { 57 log.Panicf("splice: %v", err) 58 } 59 60 devNullFD = uintptr(fd) 61 } 62 63 // copy & paste from syscall. 64 func fcntl(fd uintptr, cmd int, arg int) (val int, errno syscall.Errno) { 65 r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd), uintptr(arg)) 66 val = int(r0) 67 errno = syscall.Errno(e1) 68 return 69 } 70 71 const F_SETPIPE_SZ = 1031 72 const F_GETPIPE_SZ = 1032 73 74 func osPipe() (int, int, error) { 75 var fds [2]int 76 err := syscall.Pipe2(fds[:], syscall.O_NONBLOCK) 77 return fds[0], fds[1], err 78 } 79 80 func newSplicePair() (p *Pair, err error) { 81 p = &Pair{} 82 p.r, p.w, err = osPipe() 83 if err != nil { 84 return nil, err 85 } 86 var errNo syscall.Errno 87 p.size, errNo = fcntl(uintptr(p.r), F_GETPIPE_SZ, 0) 88 if err == syscall.EINVAL { 89 p.size = DefaultPipeSize 90 return p, nil 91 } 92 if errNo != 0 { 93 p.Close() 94 return nil, fmt.Errorf("fcntl getsize: %v", errNo) 95 } 96 return p, nil 97 }