1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
8 // - catch more errors (no first header, write after close, etc.)
17 ErrWriteTooLong = errors.New("write too long")
18 ErrFieldTooLong = errors.New("header field too long")
19 ErrWriteAfterClose = errors.New("write after close")
22 // A Writer provides sequential writing of a tar archive in POSIX.1 format.
23 // A tar archive consists of a sequence of files.
24 // Call WriteHeader to begin a new file, and then call Write to supply that file's data,
25 // writing at most hdr.Size bytes in total.
28 // tw := tar.NewWriter(w)
30 // hdr.Size = length of data in bytes
31 // // populate other hdr fields as desired
32 // if err := tw.WriteHeader(hdr); err != nil {
40 nb int64 // number of unwritten bytes for current file entry
41 pad int64 // amount of padding to write after current file entry
43 usedBinary bool // whether the binary numeric field extension was used
46 // NewWriter creates a new Writer writing to w.
47 func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
49 // Flush finishes writing the current file (optional).
50 func (tw *Writer) Flush() error {
52 for n > 0 && tw.err == nil {
58 nw, tw.err = tw.w.Write(zeroBlock[0:nr])
66 // Write s into b, terminating it with a NUL if there is room.
67 func (tw *Writer) cString(b []byte, s string) {
70 tw.err = ErrFieldTooLong
80 // Encode x as an octal ASCII string and write it into b with leading zeros.
81 func (tw *Writer) octal(b []byte, x int64) {
82 s := strconv.Itob64(x, 8)
83 // leading zeros, but leave room for a NUL.
84 for len(s)+1 < len(b) {
90 // Write x into b, either as octal or as binary (GNUtar/star extension).
91 func (tw *Writer) numeric(b []byte, x int64) {
93 s := strconv.Itob64(x, 8)
98 // Too big: use binary (big-endian).
100 for i := len(b) - 1; x > 0 && i >= 0; i-- {
104 b[0] |= 0x80 // highest bit indicates binary format
107 // WriteHeader writes hdr and prepares to accept the file's contents.
108 // WriteHeader calls Flush if it is not the first header.
109 // Calling after a Close will return ErrWriteAfterClose.
110 func (tw *Writer) WriteHeader(hdr *Header) error {
112 return ErrWriteAfterClose
121 tw.nb = int64(hdr.Size)
122 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
124 header := make([]byte, blockSize)
127 // TODO(dsymonds): handle names longer than 100 chars
128 copy(s.next(100), []byte(hdr.Name))
130 tw.octal(s.next(8), hdr.Mode) // 100:108
131 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
132 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
133 tw.numeric(s.next(12), hdr.Size) // 124:136
134 tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148
135 s.next(8) // chksum (148:156)
136 s.next(1)[0] = hdr.Typeflag // 156:157
137 tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
138 copy(s.next(8), []byte("ustar\x0000")) // 257:265
139 tw.cString(s.next(32), hdr.Uname) // 265:297
140 tw.cString(s.next(32), hdr.Gname) // 297:329
141 tw.numeric(s.next(8), hdr.Devmajor) // 329:337
142 tw.numeric(s.next(8), hdr.Devminor) // 337:345
144 // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
146 copy(header[257:265], []byte("ustar \x00"))
149 // The chksum field is terminated by a NUL and a space.
150 // This is different from the other octal fields.
151 chksum, _ := checksum(header)
152 tw.octal(header[148:155], chksum)
156 // problem with header; probably integer too big for a field.
160 _, tw.err = tw.w.Write(header)
165 // Write writes to the current entry in the tar archive.
166 // Write returns the error ErrWriteTooLong if more than
167 // hdr.Size bytes are written after WriteHeader.
168 func (tw *Writer) Write(b []byte) (n int, err error) {
170 err = ErrWriteTooLong
174 if int64(len(b)) > tw.nb {
178 n, err = tw.w.Write(b)
180 if err == nil && overwrite {
181 err = ErrWriteTooLong
188 // Close closes the tar archive, flushing any unwritten
189 // data to the underlying writer.
190 func (tw *Writer) Close() error {
191 if tw.err != nil || tw.closed {
197 // trailer: two zero blocks
198 for i := 0; i < 2; i++ {
199 _, tw.err = tw.w.Write(zeroBlock)