logginghandler/vendor/github.com/cavaliergopher/cpio/writer.go
Marvin Preuss d095180eb4
All checks were successful
continuous-integration/drone/push Build is passing
build: uses go modules for tool handling
2022-01-14 13:51:56 +01:00

141 lines
3.4 KiB
Go

package cpio
import (
"errors"
"fmt"
"io"
)
var (
// ErrWriteTooLong indicates that an attempt was made to write more than
// Header.Size bytes to the current file.
ErrWriteTooLong = errors.New("cpio: write too long")
// ErrWriteAfterClose indicates that an attempt was made to write to the
// CPIO archive after it was closed.
ErrWriteAfterClose = errors.New("cpio: write after close")
)
var trailer = &Header{
Name: headerEOF,
Links: 1,
}
var zeroBlock [4]byte
// Writer provides sequential writing of a CPIO archive. Write.WriteHeader
// begins a new file with the provided Header, and then Writer can be treated as
// an io.Writer to supply that file's data.
type Writer struct {
w io.Writer
nb int64 // number of unwritten bytes for current file entry
pad int64 // amount of padding to write after current file entry
inode int64
err error
closed bool
}
// NewWriter creates a new Writer writing to w.
func NewWriter(w io.Writer) *Writer {
return &Writer{w: w}
}
// Flush finishes writing the current file's block padding. The current file
// must be fully written before Flush can be called.
//
// This is unnecessary as the next call to WriteHeader or Close will implicitly
// flush out the file's padding.
func (w *Writer) Flush() error {
if w.nb > 0 {
w.err = fmt.Errorf("cpio: missed writing %d bytes", w.nb)
return w.err
}
_, w.err = w.w.Write(zeroBlock[:w.pad])
if w.err != nil {
return w.err
}
w.nb = 0
w.pad = 0
return w.err
}
// WriteHeader writes hdr and prepares to accept the file's contents. The
// Header.Size determines how many bytes can be written for the next file. If
// the current file is not fully written, then this returns an error. This
// implicitly flushes any padding necessary before writing the header.
func (w *Writer) WriteHeader(hdr *Header) (err error) {
if w.closed {
return ErrWriteAfterClose
}
if w.err == nil {
w.Flush()
}
if w.err != nil {
return w.err
}
if hdr.Name != headerEOF {
// TODO: should we be mutating hdr here?
// ensure all inodes are unique
w.inode++
if hdr.Inode == 0 {
hdr.Inode = w.inode
}
// ensure file type is set
if hdr.Mode&^ModePerm == 0 {
hdr.Mode |= TypeReg
}
// ensure regular files have at least 1 inbound link
if hdr.Links < 1 && hdr.Mode.IsRegular() {
hdr.Links = 1
}
}
w.nb = hdr.Size
w.pad, w.err = writeSVR4Header(w.w, hdr)
return
}
// Write writes to the current file in the CPIO archive. Write returns the error
// ErrWriteTooLong if more than Header.Size bytes are written after WriteHeader.
//
// Calling Write on special types like TypeLink, TypeSymlink, TypeChar,
// TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless of
// what the Header.Size claims.
func (w *Writer) Write(p []byte) (n int, err error) {
if w.closed {
err = ErrWriteAfterClose
return
}
overwrite := false
if int64(len(p)) > w.nb {
p = p[0:w.nb]
overwrite = true
}
n, err = w.w.Write(p)
w.nb -= int64(n)
if err == nil && overwrite {
err = ErrWriteTooLong
return
}
w.err = err
return
}
// Close closes the CPIO archive by flushing the padding, and writing the
// footer. If the current file (from a prior call to WriteHeader) is not fully
// written, then this returns an error.
func (w *Writer) Close() error {
if w.err != nil || w.closed {
return w.err
}
w.err = w.WriteHeader(trailer)
if w.err != nil {
return w.err
}
w.Flush()
w.closed = true
return w.err
}