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  }