add pipe util

This commit is contained in:
criyle 2019-09-01 19:49:18 -07:00
parent 2968ef7e27
commit 1df3228aec
3 changed files with 73 additions and 7 deletions

View File

@ -52,6 +52,7 @@ Default file access syscall check:
- cgroup: creates cgroup directories and collects resource usage / limits
- mount: provides utility function that wrappers mount syscall
- rlimit: provides utility function that defines rlimit syscall
- pipe: provides wrapper to collect all written content through pipe
## Packages

View File

@ -1,3 +1,6 @@
// +build linux
// Package memfd provides a wrapper to linux memfd
package memfd
import (
@ -8,21 +11,31 @@ import (
"golang.org/x/sys/unix"
)
const createFlag = unix.MFD_CLOEXEC | unix.MFD_ALLOW_SEALING
const roSeal = unix.F_SEAL_SEAL | unix.F_SEAL_SHRINK | unix.F_SEAL_GROW | unix.F_SEAL_WRITE
// DupToMemfd convers a fd to sealed memfd for given name
func DupToMemfd(name string, fin *os.File) (*os.File, error) {
fd, err := unix.MemfdCreate(name, unix.MFD_CLOEXEC|unix.MFD_ALLOW_SEALING)
// New creates a new memfd, caller need to close the file
func New(name string) (*os.File, error) {
fd, err := unix.MemfdCreate(name, createFlag)
if err != nil {
return nil, fmt.Errorf("DupToMemfd: memfd_create failed(%v)", err)
return nil, fmt.Errorf("memfd: memfd_create failed %v", err)
}
file := os.NewFile(uintptr(fd), name)
if file == nil {
unix.Close(fd)
return nil, fmt.Errorf("DupToMemfd: memfd new file failed")
return nil, fmt.Errorf("memfd: MewFile failed")
}
// TODO: Send file might be more efficient here
if _, err = io.Copy(file, fin); err != nil {
return file, nil
}
// DupToMemfd reads content from reader to sealed (readonly) memfd for given name
func DupToMemfd(name string, reader io.Reader) (*os.File, error) {
file, err := New(name)
if err != nil {
return nil, fmt.Errorf("DupToMemfd: new memfd failed %v", err)
}
// linux syscall sendfile might be more efficient here if reader is a file
if _, err = io.Copy(file, reader); err != nil {
file.Close()
return nil, fmt.Errorf("DupToMemfd: memfd io copy failed(%v)", err)
}

52
pkg/pipe/buffer.go Normal file
View File

@ -0,0 +1,52 @@
// Package pipe provides a wrapper to create a pipe and
// read at most max content from the reader side
package pipe
import (
"bytes"
"io"
"os"
)
// Buffer is used to create a writable pipe and read
// at most max bytes to a buffer
type Buffer struct {
W *os.File
Max int64
Buffer *bytes.Buffer
Done <-chan struct{}
}
// NewPipe create a pipe with a goroutine to copy its read-end to writer
// returns the write end and signal for finish
// caller need to close w
func NewPipe(writer io.Writer, n int64) (<-chan struct{}, *os.File, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, nil, err
}
done := make(chan struct{})
go func() {
defer close(done)
defer r.Close()
io.CopyN(writer, r, int64(n))
}()
return done, w, nil
}
// NewBuffer creates a os pipe, caller need to
// caller need to close w
// Notice: if rely on doen for finish, w need be closed in parent process
func NewBuffer(max int64) (*Buffer, error) {
buffer := new(bytes.Buffer)
done, w, err := NewPipe(buffer, max+1)
if err != nil {
return nil, err
}
return &Buffer{
W: w,
Max: max,
Buffer: buffer,
Done: done,
}, nil
}