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