diff --git a/README.md b/README.md index f22dd25..98d6a30 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/pkg/memfd/memfd.go b/pkg/memfd/memfd.go index 4efac71..84f43d9 100644 --- a/pkg/memfd/memfd.go +++ b/pkg/memfd/memfd.go @@ -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) } diff --git a/pkg/pipe/buffer.go b/pkg/pipe/buffer.go new file mode 100644 index 0000000..aff46a7 --- /dev/null +++ b/pkg/pipe/buffer.go @@ -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 +}