github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/src/archive/tar/writer.go (about) 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 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "path" 12 "sort" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 // Writer provides sequential writing of a tar archive. 19 // Write.WriteHeader begins a new file with the provided Header, 20 // and then Writer can be treated as an io.Writer to supply that file's data. 21 type Writer struct { 22 w io.Writer 23 pad int64 // Amount of padding to write after current file entry 24 curr fileWriter // Writer for current file entry 25 hdr Header // Shallow copy of Header that is safe for mutations 26 blk block // Buffer to use as temporary local storage 27 28 // err is a persistent error. 29 // It is only the responsibility of every exported method of Writer to 30 // ensure that this error is sticky. 31 err error 32 } 33 34 // NewWriter creates a new Writer writing to w. 35 func NewWriter(w io.Writer) *Writer { 36 return &Writer{w: w, curr: ®FileWriter{w, 0}} 37 } 38 39 type fileWriter interface { 40 io.Writer 41 fileState 42 43 FillZeros(n int64) (int64, error) 44 } 45 46 // Flush finishes writing the current file's block padding. 47 // The current file must be fully written before Flush can be called. 48 // 49 // Deprecated: This is unnecessary as the next call to WriteHeader or Close 50 // will implicitly flush out the file's padding. 51 func (tw *Writer) Flush() error { 52 if tw.err != nil { 53 return tw.err 54 } 55 if nb := tw.curr.Remaining(); nb > 0 { 56 return fmt.Errorf("tar: missed writing %d bytes", nb) 57 } 58 if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { 59 return tw.err 60 } 61 tw.pad = 0 62 return nil 63 } 64 65 // WriteHeader writes hdr and prepares to accept the file's contents. 66 // The Header.Size determines how many bytes can be written for the next file. 67 // If the current file is not fully written, then this returns an error. 68 // This implicitly flushes any padding necessary before writing the header. 69 func (tw *Writer) WriteHeader(hdr *Header) error { 70 if err := tw.Flush(); err != nil { 71 return err 72 } 73 74 tw.hdr = *hdr // Shallow copy of Header 75 allowedFormats, paxHdrs, err := tw.hdr.allowedFormats() 76 switch { 77 case allowedFormats.has(FormatUSTAR): 78 tw.err = tw.writeUSTARHeader(&tw.hdr) 79 return tw.err 80 case allowedFormats.has(FormatPAX): 81 tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs) 82 return tw.err 83 case allowedFormats.has(FormatGNU): 84 tw.err = tw.writeGNUHeader(&tw.hdr) 85 return tw.err 86 default: 87 return err // Non-fatal error 88 } 89 } 90 91 func (tw *Writer) writeUSTARHeader(hdr *Header) error { 92 // Check if we can use USTAR prefix/suffix splitting. 93 var namePrefix string 94 if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok { 95 namePrefix, hdr.Name = prefix, suffix 96 } 97 98 // Pack the main header. 99 var f formatter 100 blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) 101 f.formatString(blk.USTAR().Prefix(), namePrefix) 102 blk.SetFormat(FormatUSTAR) 103 if f.err != nil { 104 return f.err // Should never happen since header is validated 105 } 106 return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag) 107 } 108 109 func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { 110 realName, realSize := hdr.Name, hdr.Size 111 112 // Handle sparse files. 113 var spd sparseDatas 114 var spb []byte 115 if len(hdr.SparseHoles) > 0 { 116 sph := append([]SparseEntry{}, hdr.SparseHoles...) // Copy sparse map 117 sph = alignSparseEntries(sph, hdr.Size) 118 spd = invertSparseEntries(sph, hdr.Size) 119 120 // Format the sparse map. 121 hdr.Size = 0 // Replace with encoded size 122 spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n') 123 for _, s := range spd { 124 hdr.Size += s.Length 125 spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n') 126 spb = append(strconv.AppendInt(spb, s.Length, 10), '\n') 127 } 128 pad := blockPadding(int64(len(spb))) 129 spb = append(spb, zeroBlock[:pad]...) 130 hdr.Size += int64(len(spb)) // Accounts for encoded sparse map 131 132 // Add and modify appropriate PAX records. 133 dir, file := path.Split(realName) 134 hdr.Name = path.Join(dir, "GNUSparseFile.0", file) 135 paxHdrs[paxGNUSparseMajor] = "1" 136 paxHdrs[paxGNUSparseMinor] = "0" 137 paxHdrs[paxGNUSparseName] = realName 138 paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10) 139 paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10) 140 delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName 141 } 142 143 // Write PAX records to the output. 144 isGlobal := hdr.Typeflag == TypeXGlobalHeader 145 if len(paxHdrs) > 0 || isGlobal { 146 // Sort keys for deterministic ordering. 147 var keys []string 148 for k := range paxHdrs { 149 keys = append(keys, k) 150 } 151 sort.Strings(keys) 152 153 // Write each record to a buffer. 154 var buf bytes.Buffer 155 for _, k := range keys { 156 rec, err := formatPAXRecord(k, paxHdrs[k]) 157 if err != nil { 158 return err 159 } 160 buf.WriteString(rec) 161 } 162 163 // Write the extended header file. 164 var name string 165 var flag byte 166 if isGlobal { 167 name = "GlobalHead.0.0" 168 flag = TypeXGlobalHeader 169 } else { 170 dir, file := path.Split(realName) 171 name = path.Join(dir, "PaxHeaders.0", file) 172 flag = TypeXHeader 173 } 174 data := buf.String() 175 if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { 176 return err // Global headers return here 177 } 178 } 179 180 // Pack the main header. 181 var f formatter // Ignore errors since they are expected 182 fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } 183 blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) 184 blk.SetFormat(FormatPAX) 185 if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { 186 return err 187 } 188 189 // Write the sparse map and setup the sparse writer if necessary. 190 if len(spd) > 0 { 191 // Use tw.curr since the sparse map is accounted for in hdr.Size. 192 if _, err := tw.curr.Write(spb); err != nil { 193 return err 194 } 195 tw.curr = &sparseFileWriter{tw.curr, spd, 0} 196 } 197 return nil 198 } 199 200 func (tw *Writer) writeGNUHeader(hdr *Header) error { 201 // Use long-link files if Name or Linkname exceeds the field size. 202 const longName = "././@LongLink" 203 if len(hdr.Name) > nameSize { 204 data := hdr.Name + "\x00" 205 if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil { 206 return err 207 } 208 } 209 if len(hdr.Linkname) > nameSize { 210 data := hdr.Linkname + "\x00" 211 if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil { 212 return err 213 } 214 } 215 216 // Pack the main header. 217 var f formatter // Ignore errors since they are expected 218 var spd sparseDatas 219 var spb []byte 220 blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) 221 if !hdr.AccessTime.IsZero() { 222 f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) 223 } 224 if !hdr.ChangeTime.IsZero() { 225 f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) 226 } 227 if hdr.Typeflag == TypeGNUSparse { 228 sph := append([]SparseEntry{}, hdr.SparseHoles...) // Copy sparse map 229 sph = alignSparseEntries(sph, hdr.Size) 230 spd = invertSparseEntries(sph, hdr.Size) 231 232 // Format the sparse map. 233 formatSPD := func(sp sparseDatas, sa sparseArray) sparseDatas { 234 for i := 0; len(sp) > 0 && i < sa.MaxEntries(); i++ { 235 f.formatNumeric(sa.Entry(i).Offset(), sp[0].Offset) 236 f.formatNumeric(sa.Entry(i).Length(), sp[0].Length) 237 sp = sp[1:] 238 } 239 if len(sp) > 0 { 240 sa.IsExtended()[0] = 1 241 } 242 return sp 243 } 244 sp2 := formatSPD(spd, blk.GNU().Sparse()) 245 for len(sp2) > 0 { 246 var spHdr block 247 sp2 = formatSPD(sp2, spHdr.Sparse()) 248 spb = append(spb, spHdr[:]...) 249 } 250 251 // Update size fields in the header block. 252 realSize := hdr.Size 253 hdr.Size = 0 // Encoded size; does not account for encoded sparse map 254 for _, s := range spd { 255 hdr.Size += s.Length 256 } 257 copy(blk.V7().Size(), zeroBlock[:]) // Reset field 258 f.formatNumeric(blk.V7().Size(), hdr.Size) 259 f.formatNumeric(blk.GNU().RealSize(), realSize) 260 } 261 blk.SetFormat(FormatGNU) 262 if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { 263 return err 264 } 265 266 // Write the extended sparse map and setup the sparse writer if necessary. 267 if len(spd) > 0 { 268 // Use tw.w since the sparse map is not accounted for in hdr.Size. 269 if _, err := tw.w.Write(spb); err != nil { 270 return err 271 } 272 tw.curr = &sparseFileWriter{tw.curr, spd, 0} 273 } 274 return nil 275 } 276 277 type ( 278 stringFormatter func([]byte, string) 279 numberFormatter func([]byte, int64) 280 ) 281 282 // templateV7Plus fills out the V7 fields of a block using values from hdr. 283 // It also fills out fields (uname, gname, devmajor, devminor) that are 284 // shared in the USTAR, PAX, and GNU formats using the provided formatters. 285 // 286 // The block returned is only valid until the next call to 287 // templateV7Plus or writeRawFile. 288 func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { 289 tw.blk.Reset() 290 291 modTime := hdr.ModTime 292 if modTime.IsZero() { 293 modTime = time.Unix(0, 0) 294 } 295 296 v7 := tw.blk.V7() 297 v7.TypeFlag()[0] = hdr.Typeflag 298 fmtStr(v7.Name(), hdr.Name) 299 fmtStr(v7.LinkName(), hdr.Linkname) 300 fmtNum(v7.Mode(), hdr.Mode) 301 fmtNum(v7.UID(), int64(hdr.Uid)) 302 fmtNum(v7.GID(), int64(hdr.Gid)) 303 fmtNum(v7.Size(), hdr.Size) 304 fmtNum(v7.ModTime(), modTime.Unix()) 305 306 ustar := tw.blk.USTAR() 307 fmtStr(ustar.UserName(), hdr.Uname) 308 fmtStr(ustar.GroupName(), hdr.Gname) 309 fmtNum(ustar.DevMajor(), hdr.Devmajor) 310 fmtNum(ustar.DevMinor(), hdr.Devminor) 311 312 return &tw.blk 313 } 314 315 // writeRawFile writes a minimal file with the given name and flag type. 316 // It uses format to encode the header format and will write data as the body. 317 // It uses default values for all of the other fields (as BSD and GNU tar does). 318 func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { 319 tw.blk.Reset() 320 321 // Best effort for the filename. 322 name = toASCII(name) 323 if len(name) > nameSize { 324 name = name[:nameSize] 325 } 326 327 var f formatter 328 v7 := tw.blk.V7() 329 v7.TypeFlag()[0] = flag 330 f.formatString(v7.Name(), name) 331 f.formatOctal(v7.Mode(), 0) 332 f.formatOctal(v7.UID(), 0) 333 f.formatOctal(v7.GID(), 0) 334 f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB 335 f.formatOctal(v7.ModTime(), 0) 336 tw.blk.SetFormat(format) 337 if f.err != nil { 338 return f.err // Only occurs if size condition is violated 339 } 340 341 // Write the header and data. 342 if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil { 343 return err 344 } 345 _, err := io.WriteString(tw, data) 346 return err 347 } 348 349 // writeRawHeader writes the value of blk, regardless of its value. 350 // It sets up the Writer such that it can accept a file of the given size. 351 // If the flag is a special header-only flag, then the size is treated as zero. 352 func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error { 353 if err := tw.Flush(); err != nil { 354 return err 355 } 356 if _, err := tw.w.Write(blk[:]); err != nil { 357 return err 358 } 359 if isHeaderOnlyType(flag) { 360 size = 0 361 } 362 tw.curr = ®FileWriter{tw.w, size} 363 tw.pad = blockPadding(size) 364 return nil 365 } 366 367 // splitUSTARPath splits a path according to USTAR prefix and suffix rules. 368 // If the path is not splittable, then it will return ("", "", false). 369 func splitUSTARPath(name string) (prefix, suffix string, ok bool) { 370 length := len(name) 371 if length <= nameSize || !isASCII(name) { 372 return "", "", false 373 } else if length > prefixSize+1 { 374 length = prefixSize + 1 375 } else if name[length-1] == '/' { 376 length-- 377 } 378 379 i := strings.LastIndex(name[:length], "/") 380 nlen := len(name) - i - 1 // nlen is length of suffix 381 plen := i // plen is length of prefix 382 if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize { 383 return "", "", false 384 } 385 return name[:i], name[i+1:], true 386 } 387 388 // Write writes to the current file in the tar archive. 389 // Write returns the error ErrWriteTooLong if more than 390 // Header.Size bytes are written after WriteHeader. 391 // 392 // If the current file is sparse, then the regions marked as a hole 393 // must be written as NUL-bytes. 394 // 395 // Calling Write on special types like TypeLink, TypeSymLink, TypeChar, 396 // TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless 397 // of what the Header.Size claims. 398 func (tw *Writer) Write(b []byte) (int, error) { 399 if tw.err != nil { 400 return 0, tw.err 401 } 402 n, err := tw.curr.Write(b) 403 if err != nil && err != ErrWriteTooLong { 404 tw.err = err 405 } 406 return n, err 407 } 408 409 // TODO(dsnet): Export the Writer.FillZeros method to assist in quickly zeroing 410 // out sections of a file. This is especially useful for efficiently 411 // skipping over large holes in a sparse file. 412 413 // fillZeros writes n bytes of zeros to the current file, 414 // returning the number of bytes written. 415 // If fewer than n bytes are discarded, it returns an non-nil error, 416 // which may be ErrWriteTooLong if the current file is complete. 417 func (tw *Writer) fillZeros(n int64) (int64, error) { 418 if tw.err != nil { 419 return 0, tw.err 420 } 421 n, err := tw.curr.FillZeros(n) 422 if err != nil && err != ErrWriteTooLong { 423 tw.err = err 424 } 425 return n, err 426 } 427 428 // Close closes the tar archive by flushing the padding, and writing the footer. 429 // If the current file (from a prior call to WriteHeader) is not fully written, 430 // then this returns an error. 431 func (tw *Writer) Close() error { 432 if tw.err == ErrWriteAfterClose { 433 return nil 434 } 435 if tw.err != nil { 436 return tw.err 437 } 438 439 // Trailer: two zero blocks. 440 err := tw.Flush() 441 for i := 0; i < 2 && err == nil; i++ { 442 _, err = tw.w.Write(zeroBlock[:]) 443 } 444 445 // Ensure all future actions are invalid. 446 tw.err = ErrWriteAfterClose 447 return err // Report IO errors 448 } 449 450 // regFileWriter is a fileWriter for writing data to a regular file entry. 451 type regFileWriter struct { 452 w io.Writer // Underlying Writer 453 nb int64 // Number of remaining bytes to write 454 } 455 456 func (fw *regFileWriter) Write(b []byte) (int, error) { 457 overwrite := int64(len(b)) > fw.nb 458 if overwrite { 459 b = b[:fw.nb] 460 } 461 n, err := fw.w.Write(b) 462 fw.nb -= int64(n) 463 switch { 464 case err != nil: 465 return n, err 466 case overwrite: 467 return n, ErrWriteTooLong 468 default: 469 return n, nil 470 } 471 } 472 473 func (fw *regFileWriter) FillZeros(n int64) (int64, error) { 474 return io.CopyN(fw, zeroReader{}, n) 475 } 476 477 func (fw regFileWriter) Remaining() int64 { 478 return fw.nb 479 } 480 481 // sparseFileWriter is a fileWriter for writing data to a sparse file entry. 482 type sparseFileWriter struct { 483 fw fileWriter // Underlying fileWriter 484 sp sparseDatas // Normalized list of data fragments 485 pos int64 // Current position in sparse file 486 } 487 488 func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { 489 overwrite := int64(len(b)) > sw.Remaining() 490 if overwrite { 491 b = b[:sw.Remaining()] 492 } 493 494 b0 := b 495 endPos := sw.pos + int64(len(b)) 496 for endPos > sw.pos && err == nil { 497 var nf int // Bytes written in fragment 498 dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() 499 if sw.pos < dataStart { // In a hole fragment 500 bf := b[:min(int64(len(b)), dataStart-sw.pos)] 501 nf, err = zeroWriter{}.Write(bf) 502 } else { // In a data fragment 503 bf := b[:min(int64(len(b)), dataEnd-sw.pos)] 504 nf, err = sw.fw.Write(bf) 505 } 506 b = b[nf:] 507 sw.pos += int64(nf) 508 if sw.pos >= dataEnd && len(sw.sp) > 1 { 509 sw.sp = sw.sp[1:] // Ensure last fragment always remains 510 } 511 } 512 513 n = len(b0) - len(b) 514 switch { 515 case err == ErrWriteTooLong: 516 return n, errMissData // Not possible; implies bug in validation logic 517 case err != nil: 518 return n, err 519 case sw.Remaining() == 0 && sw.fw.Remaining() > 0: 520 return n, errUnrefData // Not possible; implies bug in validation logic 521 case overwrite: 522 return n, ErrWriteTooLong 523 default: 524 return n, nil 525 } 526 } 527 528 func (sw *sparseFileWriter) FillZeros(n int64) (int64, error) { 529 overwrite := n > sw.Remaining() 530 if overwrite { 531 n = sw.Remaining() 532 } 533 534 var realFill int64 // Number of real data bytes to fill 535 endPos := sw.pos + n 536 for endPos > sw.pos { 537 var nf int64 // Size of fragment 538 dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() 539 if sw.pos < dataStart { // In a hole fragment 540 nf = min(endPos-sw.pos, dataStart-sw.pos) 541 } else { // In a data fragment 542 nf = min(endPos-sw.pos, dataEnd-sw.pos) 543 realFill += nf 544 } 545 sw.pos += nf 546 if sw.pos >= dataEnd && len(sw.sp) > 1 { 547 sw.sp = sw.sp[1:] // Ensure last fragment always remains 548 } 549 } 550 551 _, err := sw.fw.FillZeros(realFill) 552 switch { 553 case err == ErrWriteTooLong: 554 return n, errMissData // Not possible; implies bug in validation logic 555 case err != nil: 556 return n, err 557 case sw.Remaining() == 0 && sw.fw.Remaining() > 0: 558 return n, errUnrefData // Not possible; implies bug in validation logic 559 case overwrite: 560 return n, ErrWriteTooLong 561 default: 562 return n, nil 563 } 564 } 565 566 func (sw sparseFileWriter) Remaining() int64 { 567 return sw.sp[len(sw.sp)-1].endOffset() - sw.pos 568 } 569 570 // zeroWriter may only be written with NULs, otherwise it returns errWriteHole. 571 type zeroWriter struct{} 572 573 func (zeroWriter) Write(b []byte) (int, error) { 574 for i, c := range b { 575 if c != 0 { 576 return i, errWriteHole 577 } 578 } 579 return len(b), nil 580 }