mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-06 22:18:28 +00:00
181 lines
4.2 KiB
Go
181 lines
4.2 KiB
Go
// Copyright (c) 2011 Mikkel Krautz
|
|
// The use of this source code is goverened by a BSD-style
|
|
// license that can be found in the LICENSE-file.
|
|
|
|
package ar
|
|
|
|
import (
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
// A Writer provides sequential writing of an ar archive in BSD format. It does not support
|
|
// writing in the GNU format, since the GNU-style extended filenames cannot be written sequentially.
|
|
// The BSD ar format is widely compatible with most modern ar readers out there.
|
|
//
|
|
// An ar 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.
|
|
//
|
|
// Example:
|
|
// aw := ar.NewWriter(w)
|
|
// hdr := new(ar.Header)
|
|
// hdr.Size = length of data in bytes
|
|
// // populate other hdr fields as desired
|
|
// if err := aw.WriteHeader(hdr); err != nil {
|
|
// // handle error
|
|
// }
|
|
// io.Copy(tw, data)
|
|
// tw.Close()
|
|
type Writer struct {
|
|
w io.Writer
|
|
offset int64
|
|
dataRemain int64
|
|
closed bool
|
|
}
|
|
|
|
// NewWriter creates a new Writer writing to w.
|
|
func NewWriter(w io.Writer) *Writer {
|
|
return &Writer{w, 0, 0, false}
|
|
}
|
|
|
|
// Closes the ar achive, flushing any unwritten data to the underlying writer.
|
|
func (aw *Writer) Close() error {
|
|
err := aw.Flush()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
aw.closed = true
|
|
return nil
|
|
}
|
|
|
|
// Flush finishes writing the current file (optional).
|
|
func (aw *Writer) Flush() error {
|
|
if aw.closed {
|
|
return ErrWriteAfterClose
|
|
}
|
|
if aw.offset%2 != 0 {
|
|
_, err := io.WriteString(aw.w, "\n")
|
|
aw.offset += int64(1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Write writes the current entry in the ar archive. Write returns the error ErrWriteTooLong
|
|
// if more than hdr.Size bytes are written following a call to WriteHeader.
|
|
func (aw *Writer) Write(b []byte) (n int, err error) {
|
|
if aw.closed {
|
|
return 0, ErrWriteAfterClose
|
|
}
|
|
// Overflow check
|
|
tooLong := false
|
|
if int64(len(b)) > aw.dataRemain {
|
|
b = b[0:aw.dataRemain]
|
|
tooLong = true
|
|
}
|
|
n, err = aw.w.Write(b)
|
|
aw.dataRemain -= int64(n)
|
|
// Warn if the write would have overflowed the
|
|
// space set aside for the provided data.
|
|
if err == nil && tooLong {
|
|
err = ErrWriteTooLong
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// WriteHeader writes hdr and prepares to accept the file's content. WriteHeader calls Flush to
|
|
// correctly pad the last written file. Calling after WriteHeader a Close will return ErrWriteAfterClose.
|
|
func (aw *Writer) WriteHeader(hdr *Header) (err error) {
|
|
if aw.closed {
|
|
return ErrWriteAfterClose
|
|
}
|
|
|
|
// Flush previous data write
|
|
err = aw.Flush()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If we're at the beginning of the writer, write
|
|
// the global header.
|
|
if aw.offset == 0 {
|
|
nwritten, err := io.WriteString(aw.w, globalHeader)
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var (
|
|
newName string
|
|
newSize int64
|
|
)
|
|
|
|
longFn := len(hdr.Name) > 15
|
|
if longFn {
|
|
newName = bsdLongFileNamePrefix + strconv.Itoa(len(hdr.Name))
|
|
newSize = hdr.Size + int64(len(hdr.Name))
|
|
} else {
|
|
newName = hdr.Name
|
|
newSize = hdr.Size
|
|
}
|
|
|
|
nwritten, err := io.WriteString(aw.w, encodeArString(newName, 16))
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nwritten, err = io.WriteString(aw.w, encodeArString(strconv.FormatInt(hdr.Mtime, 10), 12))
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nwritten, err = io.WriteString(aw.w, encodeArString(strconv.Itoa(hdr.Uid), 6))
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nwritten, err = io.WriteString(aw.w, encodeArString(strconv.Itoa(hdr.Gid), 6))
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nwritten, err = io.WriteString(aw.w, encodeArString(strconv.FormatInt(hdr.Mode, 8), 8))
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nwritten, err = io.WriteString(aw.w, encodeArString(strconv.FormatInt(newSize, 10), 10))
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nwritten, err = io.WriteString(aw.w, fileHeaderMagic)
|
|
aw.offset += int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if longFn {
|
|
nwritten, err = io.WriteString(aw.w, hdr.Name)
|
|
aw.offset += int64(nwritten)
|
|
aw.dataRemain = newSize - int64(nwritten)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
aw.dataRemain = newSize
|
|
}
|
|
|
|
return nil
|
|
}
|