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