PR libgcc/55451
[official-gcc.git] / libgo / go / archive / tar / writer.go
blob5af504b437d0af0e94495911dd791ba048455174
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.
5 package tar
7 // TODO(dsymonds):
8 // - catch more errors (no first header, etc.)
10 import (
11 "errors"
12 "fmt"
13 "io"
14 "strconv"
15 "time"
18 var (
19 ErrWriteTooLong = errors.New("archive/tar: write too long")
20 ErrFieldTooLong = errors.New("archive/tar: header field too long")
21 ErrWriteAfterClose = errors.New("archive/tar: write after close")
24 // A Writer provides sequential writing of a tar archive in POSIX.1 format.
25 // A tar archive consists of a sequence of files.
26 // Call WriteHeader to begin a new file, and then call Write to supply that file's data,
27 // writing at most hdr.Size bytes in total.
29 // Example:
30 // tw := tar.NewWriter(w)
31 // hdr := new(tar.Header)
32 // hdr.Size = length of data in bytes
33 // // populate other hdr fields as desired
34 // if err := tw.WriteHeader(hdr); err != nil {
35 // // handle error
36 // }
37 // io.Copy(tw, data)
38 // tw.Close()
39 type Writer struct {
40 w io.Writer
41 err error
42 nb int64 // number of unwritten bytes for current file entry
43 pad int64 // amount of padding to write after current file entry
44 closed bool
45 usedBinary bool // whether the binary numeric field extension was used
48 // NewWriter creates a new Writer writing to w.
49 func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
51 // Flush finishes writing the current file (optional).
52 func (tw *Writer) Flush() error {
53 if tw.nb > 0 {
54 tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
55 return tw.err
58 n := tw.nb + tw.pad
59 for n > 0 && tw.err == nil {
60 nr := n
61 if nr > blockSize {
62 nr = blockSize
64 var nw int
65 nw, tw.err = tw.w.Write(zeroBlock[0:nr])
66 n -= int64(nw)
68 tw.nb = 0
69 tw.pad = 0
70 return tw.err
73 // Write s into b, terminating it with a NUL if there is room.
74 func (tw *Writer) cString(b []byte, s string) {
75 if len(s) > len(b) {
76 if tw.err == nil {
77 tw.err = ErrFieldTooLong
79 return
81 copy(b, s)
82 if len(s) < len(b) {
83 b[len(s)] = 0
87 // Encode x as an octal ASCII string and write it into b with leading zeros.
88 func (tw *Writer) octal(b []byte, x int64) {
89 s := strconv.FormatInt(x, 8)
90 // leading zeros, but leave room for a NUL.
91 for len(s)+1 < len(b) {
92 s = "0" + s
94 tw.cString(b, s)
97 // Write x into b, either as octal or as binary (GNUtar/star extension).
98 func (tw *Writer) numeric(b []byte, x int64) {
99 // Try octal first.
100 s := strconv.FormatInt(x, 8)
101 if len(s) < len(b) {
102 tw.octal(b, x)
103 return
105 // Too big: use binary (big-endian).
106 tw.usedBinary = true
107 for i := len(b) - 1; x > 0 && i >= 0; i-- {
108 b[i] = byte(x)
109 x >>= 8
111 b[0] |= 0x80 // highest bit indicates binary format
114 var (
115 minTime = time.Unix(0, 0)
116 // There is room for 11 octal digits (33 bits) of mtime.
117 maxTime = minTime.Add((1<<33 - 1) * time.Second)
120 // WriteHeader writes hdr and prepares to accept the file's contents.
121 // WriteHeader calls Flush if it is not the first header.
122 // Calling after a Close will return ErrWriteAfterClose.
123 func (tw *Writer) WriteHeader(hdr *Header) error {
124 if tw.closed {
125 return ErrWriteAfterClose
127 if tw.err == nil {
128 tw.Flush()
130 if tw.err != nil {
131 return tw.err
134 tw.nb = int64(hdr.Size)
135 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
137 header := make([]byte, blockSize)
138 s := slicer(header)
140 // TODO(dsymonds): handle names longer than 100 chars
141 copy(s.next(100), []byte(hdr.Name))
143 // Handle out of range ModTime carefully.
144 var modTime int64
145 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
146 modTime = hdr.ModTime.Unix()
149 tw.octal(s.next(8), hdr.Mode) // 100:108
150 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
151 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
152 tw.numeric(s.next(12), hdr.Size) // 124:136
153 tw.numeric(s.next(12), modTime) // 136:148
154 s.next(8) // chksum (148:156)
155 s.next(1)[0] = hdr.Typeflag // 156:157
156 tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
157 copy(s.next(8), []byte("ustar\x0000")) // 257:265
158 tw.cString(s.next(32), hdr.Uname) // 265:297
159 tw.cString(s.next(32), hdr.Gname) // 297:329
160 tw.numeric(s.next(8), hdr.Devmajor) // 329:337
161 tw.numeric(s.next(8), hdr.Devminor) // 337:345
163 // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
164 if tw.usedBinary {
165 copy(header[257:265], []byte("ustar \x00"))
168 // The chksum field is terminated by a NUL and a space.
169 // This is different from the other octal fields.
170 chksum, _ := checksum(header)
171 tw.octal(header[148:155], chksum)
172 header[155] = ' '
174 if tw.err != nil {
175 // problem with header; probably integer too big for a field.
176 return tw.err
179 _, tw.err = tw.w.Write(header)
181 return tw.err
184 // Write writes to the current entry in the tar archive.
185 // Write returns the error ErrWriteTooLong if more than
186 // hdr.Size bytes are written after WriteHeader.
187 func (tw *Writer) Write(b []byte) (n int, err error) {
188 if tw.closed {
189 err = ErrWriteTooLong
190 return
192 overwrite := false
193 if int64(len(b)) > tw.nb {
194 b = b[0:tw.nb]
195 overwrite = true
197 n, err = tw.w.Write(b)
198 tw.nb -= int64(n)
199 if err == nil && overwrite {
200 err = ErrWriteTooLong
201 return
203 tw.err = err
204 return
207 // Close closes the tar archive, flushing any unwritten
208 // data to the underlying writer.
209 func (tw *Writer) Close() error {
210 if tw.err != nil || tw.closed {
211 return tw.err
213 tw.Flush()
214 tw.closed = true
215 if tw.err != nil {
216 return tw.err
219 // trailer: two zero blocks
220 for i := 0; i < 2; i++ {
221 _, tw.err = tw.w.Write(zeroBlock)
222 if tw.err != nil {
223 break
226 return tw.err