129 lines
2.7 KiB
Go
129 lines
2.7 KiB
Go
package cpio
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
var (
|
|
ErrWriteTooLong = errors.New("cpio: write too long")
|
|
ErrWriteAfterClose = errors.New("cpio: write after close")
|
|
)
|
|
|
|
var trailer = &Header{
|
|
Name: string(headerEOF),
|
|
Links: 1,
|
|
}
|
|
|
|
var zeroBlock [4]byte
|
|
|
|
// A Writer provides sequential writing of a CPIO archive. A CPIO archive
|
|
// consists of a sequence of files. Call WriteHeader to begin a new file, and
|
|
// then call Write to supply that file's data, writing at most hdr.Size bytes in
|
|
// total.
|
|
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 (optional).
|
|
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.
|
|
// WriteHeader calls Flush if it is not the first header. Calling after a Close
|
|
// will return ErrWriteAfterClose.
|
|
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 |= ModeRegular
|
|
}
|
|
|
|
// 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 entry in the CPIO archive. Write returns the
|
|
// error ErrWriteTooLong if more than hdr.Size bytes are written after
|
|
// WriteHeader.
|
|
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, flushing any unwritten data to the underlying
|
|
// writer.
|
|
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
|
|
}
|