LEFT | RIGHT |
(no file at all) | |
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") |
| 26 errNameTooLong = errors.New("archive/tar: name too long") |
22 ) | 27 ) |
23 | 28 |
24 // A Writer provides sequential writing of a tar archive in POSIX.1 format. | 29 // A Writer provides sequential writing of a tar archive in POSIX.1 format. |
25 // A tar archive consists of a sequence of files. | 30 // 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, | 31 // 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. | 32 // writing at most hdr.Size bytes in total. |
28 type Writer struct { | 33 type Writer struct { |
29 w io.Writer | 34 w io.Writer |
30 err error | 35 err error |
31 nb int64 // number of unwritten bytes for current file entry | 36 nb int64 // number of unwritten bytes for current file entry |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
112 func (tw *Writer) WriteHeader(hdr *Header) error { | 117 func (tw *Writer) WriteHeader(hdr *Header) error { |
113 if tw.closed { | 118 if tw.closed { |
114 return ErrWriteAfterClose | 119 return ErrWriteAfterClose |
115 } | 120 } |
116 if tw.err == nil { | 121 if tw.err == nil { |
117 tw.Flush() | 122 tw.Flush() |
118 } | 123 } |
119 if tw.err != nil { | 124 if tw.err != nil { |
120 return tw.err | 125 return tw.err |
121 } | 126 } |
122 | 127 » // Decide whether or not to use PAX extensions |
| 128 » // TODO(shanemhansen): we might want to use PAX headers for |
| 129 » // subsecond time resolution, but for now let's just capture |
| 130 » // the long name/long symlink use case. |
| 131 » suffix := hdr.Name |
| 132 » prefix := "" |
| 133 » if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize { |
| 134 » » var err error |
| 135 » » prefix, suffix, err = tw.splitUSTARLongName(hdr.Name) |
| 136 » » // Either we were unable to pack the long name into ustar format |
| 137 » » // or the link name is too long; use PAX headers. |
| 138 » » if err == errNameTooLong || len(hdr.Linkname) > fileNameSize { |
| 139 » » » if err := tw.writePAXHeader(hdr); err != nil { |
| 140 » » » » return err |
| 141 » » » } |
| 142 » » } else if err != nil { |
| 143 » » » return err |
| 144 » » } |
| 145 » } |
123 tw.nb = int64(hdr.Size) | 146 tw.nb = int64(hdr.Size) |
124 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two | 147 tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two |
125 | 148 |
126 header := make([]byte, blockSize) | 149 header := make([]byte, blockSize) |
127 s := slicer(header) | 150 s := slicer(header) |
128 | 151 » tw.cString(s.next(fileNameSize), suffix) |
129 » // TODO(dsymonds): handle names longer than 100 chars | |
130 » copy(s.next(100), []byte(hdr.Name)) | |
131 | 152 |
132 // Handle out of range ModTime carefully. | 153 // Handle out of range ModTime carefully. |
133 var modTime int64 | 154 var modTime int64 |
134 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) { | 155 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) { |
135 modTime = hdr.ModTime.Unix() | 156 modTime = hdr.ModTime.Unix() |
136 } | 157 } |
137 | 158 |
138 tw.octal(s.next(8), hdr.Mode) // 100:108 | 159 tw.octal(s.next(8), hdr.Mode) // 100:108 |
139 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 | 160 tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 |
140 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 | 161 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 |
141 tw.numeric(s.next(12), hdr.Size) // 124:136 | 162 tw.numeric(s.next(12), hdr.Size) // 124:136 |
142 tw.numeric(s.next(12), modTime) // 136:148 | 163 tw.numeric(s.next(12), modTime) // 136:148 |
143 s.next(8) // chksum (148:156) | 164 s.next(8) // chksum (148:156) |
144 s.next(1)[0] = hdr.Typeflag // 156:157 | 165 s.next(1)[0] = hdr.Typeflag // 156:157 |
145 tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) | 166 tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) |
146 copy(s.next(8), []byte("ustar\x0000")) // 257:265 | 167 copy(s.next(8), []byte("ustar\x0000")) // 257:265 |
147 tw.cString(s.next(32), hdr.Uname) // 265:297 | 168 tw.cString(s.next(32), hdr.Uname) // 265:297 |
148 tw.cString(s.next(32), hdr.Gname) // 297:329 | 169 tw.cString(s.next(32), hdr.Gname) // 297:329 |
149 tw.numeric(s.next(8), hdr.Devmajor) // 329:337 | 170 tw.numeric(s.next(8), hdr.Devmajor) // 329:337 |
150 tw.numeric(s.next(8), hdr.Devminor) // 337:345 | 171 tw.numeric(s.next(8), hdr.Devminor) // 337:345 |
151 | 172 » tw.cString(s.next(155), prefix) // 345:500 |
152 // Use the GNU magic instead of POSIX magic if we used any GNU extension
s. | 173 // Use the GNU magic instead of POSIX magic if we used any GNU extension
s. |
153 if tw.usedBinary { | 174 if tw.usedBinary { |
154 copy(header[257:265], []byte("ustar \x00")) | 175 copy(header[257:265], []byte("ustar \x00")) |
| 176 } |
| 177 // Use the ustar magic if we used ustar long names. |
| 178 if len(prefix) > 0 { |
| 179 copy(header[257:265], []byte("ustar\000")) |
155 } | 180 } |
156 | 181 |
157 // The chksum field is terminated by a NUL and a space. | 182 // The chksum field is terminated by a NUL and a space. |
158 // This is different from the other octal fields. | 183 // This is different from the other octal fields. |
159 chksum, _ := checksum(header) | 184 chksum, _ := checksum(header) |
160 tw.octal(header[148:155], chksum) | 185 tw.octal(header[148:155], chksum) |
161 header[155] = ' ' | 186 header[155] = ' ' |
162 | 187 |
163 if tw.err != nil { | 188 if tw.err != nil { |
164 // problem with header; probably integer too big for a field. | 189 // problem with header; probably integer too big for a field. |
165 return tw.err | 190 return tw.err |
166 } | 191 } |
167 | 192 |
168 _, tw.err = tw.w.Write(header) | 193 _, tw.err = tw.w.Write(header) |
169 | 194 |
170 return tw.err | 195 return tw.err |
| 196 } |
| 197 |
| 198 // writeUSTARLongName splits a USTAR long name hdr.Name. |
| 199 // name must be < 256 characters. errNameTooLong is returned |
| 200 // if hdr.Name can't be split. The splitting heuristic |
| 201 // is compatible with gnu tar. |
| 202 func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
ror) { |
| 203 length := len(name) |
| 204 if length > fileNamePrefixSize+1 { |
| 205 length = fileNamePrefixSize + 1 |
| 206 } else if name[length-1] == '/' { |
| 207 length-- |
| 208 } |
| 209 i := strings.LastIndex(name[:length], "/") |
| 210 nlen := length - i - 1 |
| 211 if i <= 0 || nlen > fileNameSize || nlen == 0 { |
| 212 err = errNameTooLong |
| 213 return |
| 214 } |
| 215 prefix, suffix = name[:i], name[i+1:] |
| 216 return |
| 217 } |
| 218 |
| 219 // writePaxHeader writes an extended pax header to the |
| 220 // archive. |
| 221 func (tw *Writer) writePAXHeader(hdr *Header) error { |
| 222 // Prepare extended header |
| 223 ext := new(Header) |
| 224 ext.Typeflag = TypeXHeader |
| 225 // Setting ModTime is required for reader parsing to |
| 226 // succeed, and seems harmless enough. |
| 227 ext.ModTime = hdr.ModTime |
| 228 // The spec asks that we namespace our pseudo files |
| 229 // with the current pid. |
| 230 pid := os.Getpid() |
| 231 dir, file := path.Split(hdr.Name) |
| 232 ext.Name = path.Join(dir, |
| 233 fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100] |
| 234 // Construct the body |
| 235 var buf bytes.Buffer |
| 236 if len(hdr.Name) > fileNameSize { |
| 237 fmt.Fprint(&buf, paxHeader("path="+hdr.Name)) |
| 238 } |
| 239 if len(hdr.Linkname) > fileNameSize { |
| 240 fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname)) |
| 241 } |
| 242 ext.Size = int64(len(buf.Bytes())) |
| 243 if err := tw.WriteHeader(ext); err != nil { |
| 244 return err |
| 245 } |
| 246 if _, err := tw.Write(buf.Bytes()); err != nil { |
| 247 return err |
| 248 } |
| 249 if err := tw.Flush(); err != nil { |
| 250 return err |
| 251 } |
| 252 return nil |
| 253 } |
| 254 |
| 255 // paxHeader formats a single pax record, prefixing it with the appropriate leng
th |
| 256 func paxHeader(msg string) string { |
| 257 const padding = 2 // Extra padding for space and newline |
| 258 size := len(msg) + padding |
| 259 size += len(strconv.Itoa(size)) |
| 260 record := fmt.Sprintf("%d %s\n", size, msg) |
| 261 if len(record) != size { |
| 262 // Final adjustment if adding size increased |
| 263 // the number of digits in size |
| 264 size = len(record) |
| 265 record = fmt.Sprintf("%d %s\n", size, msg) |
| 266 } |
| 267 return record |
171 } | 268 } |
172 | 269 |
173 // Write writes to the current entry in the tar archive. | 270 // Write writes to the current entry in the tar archive. |
174 // Write returns the error ErrWriteTooLong if more than | 271 // Write returns the error ErrWriteTooLong if more than |
175 // hdr.Size bytes are written after WriteHeader. | 272 // hdr.Size bytes are written after WriteHeader. |
176 func (tw *Writer) Write(b []byte) (n int, err error) { | 273 func (tw *Writer) Write(b []byte) (n int, err error) { |
177 if tw.closed { | 274 if tw.closed { |
178 err = ErrWriteTooLong | 275 err = ErrWriteTooLong |
179 return | 276 return |
180 } | 277 } |
(...skipping 26 matching lines...) Expand all Loading... |
207 | 304 |
208 // trailer: two zero blocks | 305 // trailer: two zero blocks |
209 for i := 0; i < 2; i++ { | 306 for i := 0; i < 2; i++ { |
210 _, tw.err = tw.w.Write(zeroBlock) | 307 _, tw.err = tw.w.Write(zeroBlock) |
211 if tw.err != nil { | 308 if tw.err != nil { |
212 break | 309 break |
213 } | 310 } |
214 } | 311 } |
215 return tw.err | 312 return tw.err |
216 } | 313 } |
LEFT | RIGHT |