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 }