OSDN Git Service

b9310b3f189dc080833d367727a96de97ecfdfe0
[pf3gnuchains/gcc-fork.git] / libgo / go / 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, write after close, etc.)
9
10 import (
11         "errors"
12         "io"
13         "strconv"
14 )
15
16 var (
17         ErrWriteTooLong    = errors.New("write too long")
18         ErrFieldTooLong    = errors.New("header field too long")
19         ErrWriteAfterClose = errors.New("write after close")
20 )
21
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.
26 //
27 // Example:
28 //      tw := tar.NewWriter(w)
29 //      hdr := new(Header)
30 //      hdr.Size = length of data in bytes
31 //      // populate other hdr fields as desired
32 //      if err := tw.WriteHeader(hdr); err != nil {
33 //              // handle error
34 //      }
35 //      io.Copy(tw, data)
36 //      tw.Close()
37 type Writer struct {
38         w          io.Writer
39         err        error
40         nb         int64 // number of unwritten bytes for current file entry
41         pad        int64 // amount of padding to write after current file entry
42         closed     bool
43         usedBinary bool // whether the binary numeric field extension was used
44 }
45
46 // NewWriter creates a new Writer writing to w.
47 func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
48
49 // Flush finishes writing the current file (optional).
50 func (tw *Writer) Flush() error {
51         n := tw.nb + tw.pad
52         for n > 0 && tw.err == nil {
53                 nr := n
54                 if nr > blockSize {
55                         nr = blockSize
56                 }
57                 var nw int
58                 nw, tw.err = tw.w.Write(zeroBlock[0:nr])
59                 n -= int64(nw)
60         }
61         tw.nb = 0
62         tw.pad = 0
63         return tw.err
64 }
65
66 // Write s into b, terminating it with a NUL if there is room.
67 func (tw *Writer) cString(b []byte, s string) {
68         if len(s) > len(b) {
69                 if tw.err == nil {
70                         tw.err = ErrFieldTooLong
71                 }
72                 return
73         }
74         copy(b, s)
75         if len(s) < len(b) {
76                 b[len(s)] = 0
77         }
78 }
79
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) {
85                 s = "0" + s
86         }
87         tw.cString(b, s)
88 }
89
90 // Write x into b, either as octal or as binary (GNUtar/star extension).
91 func (tw *Writer) numeric(b []byte, x int64) {
92         // Try octal first.
93         s := strconv.Itob64(x, 8)
94         if len(s) < len(b) {
95                 tw.octal(b, x)
96                 return
97         }
98         // Too big: use binary (big-endian).
99         tw.usedBinary = true
100         for i := len(b) - 1; x > 0 && i >= 0; i-- {
101                 b[i] = byte(x)
102                 x >>= 8
103         }
104         b[0] |= 0x80 // highest bit indicates binary format
105 }
106
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 {
111         if tw.closed {
112                 return ErrWriteAfterClose
113         }
114         if tw.err == nil {
115                 tw.Flush()
116         }
117         if tw.err != nil {
118                 return tw.err
119         }
120
121         tw.nb = int64(hdr.Size)
122         tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
123
124         header := make([]byte, blockSize)
125         s := slicer(header)
126
127         // TODO(dsymonds): handle names longer than 100 chars
128         copy(s.next(100), []byte(hdr.Name))
129
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
143
144         // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
145         if tw.usedBinary {
146                 copy(header[257:265], []byte("ustar  \x00"))
147         }
148
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)
153         header[155] = ' '
154
155         if tw.err != nil {
156                 // problem with header; probably integer too big for a field.
157                 return tw.err
158         }
159
160         _, tw.err = tw.w.Write(header)
161
162         return tw.err
163 }
164
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) {
169         if tw.closed {
170                 err = ErrWriteTooLong
171                 return
172         }
173         overwrite := false
174         if int64(len(b)) > tw.nb {
175                 b = b[0:tw.nb]
176                 overwrite = true
177         }
178         n, err = tw.w.Write(b)
179         tw.nb -= int64(n)
180         if err == nil && overwrite {
181                 err = ErrWriteTooLong
182                 return
183         }
184         tw.err = err
185         return
186 }
187
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 {
192                 return tw.err
193         }
194         tw.Flush()
195         tw.closed = true
196
197         // trailer: two zero blocks
198         for i := 0; i < 2; i++ {
199                 _, tw.err = tw.w.Write(zeroBlock)
200                 if tw.err != nil {
201                         break
202                 }
203         }
204         return tw.err
205 }