Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1415)

Side by Side Diff: src/pkg/archive/tar/writer.go

Issue 7229066: archive/tar: merge 6700047 and 6814084
Patch Set: diff -r 9ff35ff05de6 https://code.google.com/p/go Created 11 years, 1 month ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pkg/archive/tar/testdata/pax.tar ('k') | src/pkg/archive/tar/writer_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package tar 5 package tar
6 6
7 // TODO(dsymonds): 7 // TODO(dsymonds):
8 // - catch more errors (no first header, etc.) 8 // - catch more errors (no first header, etc.)
9 9
10 import ( 10 import (
11 "bytes"
11 "errors" 12 "errors"
12 "fmt" 13 "fmt"
13 "io" 14 "io"
15 "os"
16 "path"
14 "strconv" 17 "strconv"
18 "strings"
15 "time" 19 "time"
16 ) 20 )
17 21
18 var ( 22 var (
19 ErrWriteTooLong = errors.New("archive/tar: write too long") 23 ErrWriteTooLong = errors.New("archive/tar: write too long")
20 ErrFieldTooLong = errors.New("archive/tar: header field too long") 24 ErrFieldTooLong = errors.New("archive/tar: header field too long")
21 ErrWriteAfterClose = errors.New("archive/tar: write after close") 25 ErrWriteAfterClose = errors.New("archive/tar: write after close")
22 ) 26 )
23 27
24 // A Writer provides sequential writing of a tar archive in POSIX.1 format. 28 // A Writer provides sequential writing of a tar archive in POSIX.1 format.
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 } 108 }
105 // Too big: use binary (big-endian). 109 // Too big: use binary (big-endian).
106 tw.usedBinary = true 110 tw.usedBinary = true
107 for i := len(b) - 1; x > 0 && i >= 0; i-- { 111 for i := len(b) - 1; x > 0 && i >= 0; i-- {
108 b[i] = byte(x) 112 b[i] = byte(x)
109 x >>= 8 113 x >>= 8
110 } 114 }
111 b[0] |= 0x80 // highest bit indicates binary format 115 b[0] |= 0x80 // highest bit indicates binary format
112 } 116 }
113 117
118 // splitName cleans s and returns a prefix and name suitable for ustar
119 // prefix and name fields. ok indicates, that s is suitable for ustar.
120 // If s is not suitable for ustar, s is returned as name and prefix is
121 // empty.
122 // The ustar prefix (155 chars) and name (100 chars) are defined to be
123 // separated by a slash. That means a reader concatenates prefix and
124 // name with a slash in between and therefore a writer can omit this
125 // slash in both fields, which yields to names up to 256 chars.
126 func splitName(s string) (prefix, name string, ok bool) {
127 trailingSlash := s[len(s)-1] == '/'
128 s = path.Clean(s)
129
130 if len(s) > 256 {
131 return "", s, false
132 }
133
134 // no prefix, just return s as the name.
135 if trailingSlash && len(s) <= 99 {
136 return "", s + "/", true
137 }
138 if !trailingSlash && len(s) <= 100 {
139 return "", s, true
140 }
141
142 // determine the largest prefix in s
143 // since a trailing slash will be omitted (due to definition of prefix
144 // and name fields in ustar) we consider the first 156 characters,
145 // instead of 155.
146 max := 156
147 if len(s) < max {
148 max = len(s)
149 }
150 i := strings.LastIndex(s[:max], "/")
151
152 // s cannot be split:
153 // i == -1: s has no slash, so we deal with a file or directory name >15 6 chars.
154 // i == 0: s starts with a slash. that yields to an empty prefix field.
155 if i <= 0 {
156 return "", s, false
157 }
158
159 // i does not point to a trailing slash in s, because the path
160 // was cleaned in the beginning. therefore determining the name
161 // without bounds check is safe.
162 prefix, name = s[:i], s[i+1:]
163 if trailingSlash {
164 name += "/"
165 }
166
167 // prefix can be stored in ustar, but name cannot be stored.
168 if len(name) > 100 {
169 return "", s, false
170 }
171 return prefix, name, true
172 }
173
114 var ( 174 var (
115 minTime = time.Unix(0, 0) 175 minTime = time.Unix(0, 0)
116 // There is room for 11 octal digits (33 bits) of mtime. 176 // There is room for 11 octal digits (33 bits) of mtime.
117 maxTime = minTime.Add((1<<33 - 1) * time.Second) 177 maxTime = minTime.Add((1<<33 - 1) * time.Second)
118 ) 178 )
119 179
120 // WriteHeader writes hdr and prepares to accept the file's contents. 180 // WriteHeader writes hdr and prepares to accept the file's contents.
121 // WriteHeader calls Flush if it is not the first header. 181 // WriteHeader calls Flush if it is not the first header.
122 // Calling after a Close will return ErrWriteAfterClose. 182 // Calling after a Close will return ErrWriteAfterClose.
123 func (tw *Writer) WriteHeader(hdr *Header) error { 183 func (tw *Writer) WriteHeader(hdr *Header) error {
124 if tw.closed { 184 if tw.closed {
125 return ErrWriteAfterClose 185 return ErrWriteAfterClose
126 } 186 }
127 if tw.err == nil { 187 if tw.err == nil {
128 tw.Flush() 188 tw.Flush()
129 } 189 }
130 if tw.err != nil { 190 if tw.err != nil {
131 return tw.err 191 return tw.err
132 } 192 }
133 193 » // Decide whether or not to use ustar prefix field or PAX extensions
194 » // TODO(shanemhansen): we might want to use PAX headers for
195 » // subsecond time resolution, but for now let's just capture
196 » // the long name/long symlink use case.
197 » prefix, name, ok := splitName(hdr.Name)
198 » if !ok && len(name) > 100 || len(hdr.Linkname) > 100 {
199 » » if err := tw.writePAXHeader(hdr); err != nil {
200 » » » return err
201 » » }
202 » }
134 tw.nb = int64(hdr.Size) 203 tw.nb = int64(hdr.Size)
135 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two 204 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
136 205
137 header := make([]byte, blockSize) 206 header := make([]byte, blockSize)
138 s := slicer(header) 207 s := slicer(header)
139 208 » copy(s.next(100), []byte(name))
140 » // TODO(dsymonds): handle names longer than 100 chars
141 » copy(s.next(100), []byte(hdr.Name))
142 209
143 // Handle out of range ModTime carefully. 210 // Handle out of range ModTime carefully.
144 var modTime int64 211 var modTime int64
145 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) { 212 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
146 modTime = hdr.ModTime.Unix() 213 modTime = hdr.ModTime.Unix()
147 } 214 }
148 215
149 tw.octal(s.next(8), hdr.Mode) // 100:108 216 tw.octal(s.next(8), hdr.Mode) // 100:108
150 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 217 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
151 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 218 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
152 tw.numeric(s.next(12), hdr.Size) // 124:136 219 tw.numeric(s.next(12), hdr.Size) // 124:136
153 tw.numeric(s.next(12), modTime) // 136:148 220 tw.numeric(s.next(12), modTime) // 136:148
154 s.next(8) // chksum (148:156) 221 s.next(8) // chksum (148:156)
155 s.next(1)[0] = hdr.Typeflag // 156:157 222 s.next(1)[0] = hdr.Typeflag // 156:157
156 tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) 223 tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
157 copy(s.next(8), []byte("ustar\x0000")) // 257:265 224 copy(s.next(8), []byte("ustar\x0000")) // 257:265
158 tw.cString(s.next(32), hdr.Uname) // 265:297 225 tw.cString(s.next(32), hdr.Uname) // 265:297
159 tw.cString(s.next(32), hdr.Gname) // 297:329 226 tw.cString(s.next(32), hdr.Gname) // 297:329
160 tw.numeric(s.next(8), hdr.Devmajor) // 329:337 227 tw.numeric(s.next(8), hdr.Devmajor) // 329:337
161 tw.numeric(s.next(8), hdr.Devminor) // 337:345 228 tw.numeric(s.next(8), hdr.Devminor) // 337:345
229 copy(s.next(155), []byte(prefix)) // 345:500
162 230
163 // Use the GNU magic instead of POSIX magic if we used any GNU extension s. 231 // Use the GNU magic instead of POSIX magic if we used any GNU extension s.
164 if tw.usedBinary { 232 if tw.usedBinary {
165 copy(header[257:265], []byte("ustar \x00")) 233 copy(header[257:265], []byte("ustar \x00"))
166 } 234 }
167 235
168 // The chksum field is terminated by a NUL and a space. 236 // The chksum field is terminated by a NUL and a space.
169 // This is different from the other octal fields. 237 // This is different from the other octal fields.
170 chksum, _ := checksum(header) 238 chksum, _ := checksum(header)
171 tw.octal(header[148:155], chksum) 239 tw.octal(header[148:155], chksum)
172 header[155] = ' ' 240 header[155] = ' '
173 241
174 if tw.err != nil { 242 if tw.err != nil {
175 // problem with header; probably integer too big for a field. 243 // problem with header; probably integer too big for a field.
176 return tw.err 244 return tw.err
177 } 245 }
178 246
179 _, tw.err = tw.w.Write(header) 247 _, tw.err = tw.w.Write(header)
180 248
181 return tw.err 249 return tw.err
182 } 250 }
183 251
252 // writePaxHeader writes an extended pax header to the
253 // archive.
254 func (tw *Writer) writePAXHeader(hdr *Header) error {
255 // Prepare extended header
256 ext := new(Header)
257 ext.Typeflag = TypeXHeader
258 // Setting ModTime is required for reader parsing to
259 // succeed, and seems harmless enough.
260 ext.ModTime = hdr.ModTime
261 // The spec asks that we namespace our pseudo files
262 // with the current pid.
263 pid := os.Getpid()
264 dir, file := path.Split(hdr.Name)
265 ext.Name = path.Join(dir,
266 fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100]
267 // Construct the body
268 var buf bytes.Buffer
269 if len(hdr.Name) > 100 {
270 fmt.Fprint(&buf, paxHeader("path="+hdr.Name))
271 }
272 if len(hdr.Linkname) > 100 {
273 fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname))
274 }
275 ext.Size = int64(len(buf.Bytes()))
276 if err := tw.WriteHeader(ext); err != nil {
277 return err
278 }
279 if _, err := tw.Write(buf.Bytes()); err != nil {
280 return err
281 }
282 if err := tw.Flush(); err != nil {
283 return err
284 }
285 return nil
286 }
287
288 // paxHeader formats a single pax record, prefixing it with the appropriate leng th
289 func paxHeader(msg string) string {
290 const padding = 2 // Extra padding for space and newline
291 size := len(msg) + padding
292 size += len(strconv.Itoa(size))
293 record := fmt.Sprintf("%d %s\n", size, msg)
294 if len(record) != size {
295 // Final adjustment if adding size increased
296 // the number of digits in size
297 size = len(record)
298 record = fmt.Sprintf("%d %s\n", size, msg)
299 }
300 return record
301 }
302
184 // Write writes to the current entry in the tar archive. 303 // Write writes to the current entry in the tar archive.
185 // Write returns the error ErrWriteTooLong if more than 304 // Write returns the error ErrWriteTooLong if more than
186 // hdr.Size bytes are written after WriteHeader. 305 // hdr.Size bytes are written after WriteHeader.
187 func (tw *Writer) Write(b []byte) (n int, err error) { 306 func (tw *Writer) Write(b []byte) (n int, err error) {
188 if tw.closed { 307 if tw.closed {
189 err = ErrWriteTooLong 308 err = ErrWriteTooLong
190 return 309 return
191 } 310 }
192 overwrite := false 311 overwrite := false
193 if int64(len(b)) > tw.nb { 312 if int64(len(b)) > tw.nb {
(...skipping 24 matching lines...) Expand all
218 337
219 // trailer: two zero blocks 338 // trailer: two zero blocks
220 for i := 0; i < 2; i++ { 339 for i := 0; i < 2; i++ {
221 _, tw.err = tw.w.Write(zeroBlock) 340 _, tw.err = tw.w.Write(zeroBlock)
222 if tw.err != nil { 341 if tw.err != nil {
223 break 342 break
224 } 343 }
225 } 344 }
226 return tw.err 345 return tw.err
227 } 346 }
OLDNEW
« no previous file with comments | « src/pkg/archive/tar/testdata/pax.tar ('k') | src/pkg/archive/tar/writer_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b