Marvin Preuss
d095180eb4
All checks were successful
continuous-integration/drone/push Build is passing
141 lines
3.4 KiB
Go
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
|
|
}
|