1
2
3
4
5 package tar
6
7 import (
8 "errors"
9 "fmt"
10 "io"
11 "io/fs"
12 "maps"
13 "path"
14 "slices"
15 "strings"
16 "time"
17 )
18
19
20
21
22 type Writer struct {
23 w io.Writer
24 pad int64
25 curr fileWriter
26 hdr Header
27 blk block
28
29
30
31
32 err error
33 }
34
35
36 func NewWriter(w io.Writer) *Writer {
37 return &Writer{w: w, curr: ®FileWriter{w, 0}}
38 }
39
40 type fileWriter interface {
41 io.Writer
42 fileState
43
44 ReadFrom(io.Reader) (int64, error)
45 }
46
47
48
49
50
51
52 func (tw *Writer) Flush() error {
53 if tw.err != nil {
54 return tw.err
55 }
56 if nb := tw.curr.logicalRemaining(); nb > 0 {
57 return fmt.Errorf("archive/tar: missed writing %d bytes", nb)
58 }
59 if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil {
60 return tw.err
61 }
62 tw.pad = 0
63 return nil
64 }
65
66
67
68
69
70 func (tw *Writer) WriteHeader(hdr *Header) error {
71 if err := tw.Flush(); err != nil {
72 return err
73 }
74 tw.hdr = *hdr
75
76
77
78 if tw.hdr.Typeflag == TypeRegA {
79 if strings.HasSuffix(tw.hdr.Name, "/") {
80 tw.hdr.Typeflag = TypeDir
81 } else {
82 tw.hdr.Typeflag = TypeReg
83 }
84 }
85
86
87
88
89
90
91 if tw.hdr.Format == FormatUnknown {
92 tw.hdr.ModTime = tw.hdr.ModTime.Round(time.Second)
93 tw.hdr.AccessTime = time.Time{}
94 tw.hdr.ChangeTime = time.Time{}
95 }
96
97 allowedFormats, paxHdrs, err := tw.hdr.allowedFormats()
98 switch {
99 case allowedFormats.has(FormatUSTAR):
100 tw.err = tw.writeUSTARHeader(&tw.hdr)
101 return tw.err
102 case allowedFormats.has(FormatPAX):
103 tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs)
104 return tw.err
105 case allowedFormats.has(FormatGNU):
106 tw.err = tw.writeGNUHeader(&tw.hdr)
107 return tw.err
108 default:
109 return err
110 }
111 }
112
113 func (tw *Writer) writeUSTARHeader(hdr *Header) error {
114
115 var namePrefix string
116 if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok {
117 namePrefix, hdr.Name = prefix, suffix
118 }
119
120
121 var f formatter
122 blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
123 f.formatString(blk.toUSTAR().prefix(), namePrefix)
124 blk.setFormat(FormatUSTAR)
125 if f.err != nil {
126 return f.err
127 }
128 return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag)
129 }
130
131 func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
132 realName, realSize := hdr.Name, hdr.Size
133
134
135
136
168 _ = realSize
169
170
171 isGlobal := hdr.Typeflag == TypeXGlobalHeader
172 if len(paxHdrs) > 0 || isGlobal {
173
174 var buf strings.Builder
175
176 for _, k := range slices.Sorted(maps.Keys(paxHdrs)) {
177 rec, err := formatPAXRecord(k, paxHdrs[k])
178 if err != nil {
179 return err
180 }
181 buf.WriteString(rec)
182 }
183
184
185 var name string
186 var flag byte
187 if isGlobal {
188 name = realName
189 if name == "" {
190 name = "GlobalHead.0.0"
191 }
192 flag = TypeXGlobalHeader
193 } else {
194 dir, file := path.Split(realName)
195 name = path.Join(dir, "PaxHeaders.0", file)
196 flag = TypeXHeader
197 }
198 data := buf.String()
199 if len(data) > maxSpecialFileSize {
200 return ErrFieldTooLong
201 }
202 if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
203 return err
204 }
205 }
206
207
208 var f formatter
209 fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) }
210 blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal)
211 blk.setFormat(FormatPAX)
212 if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
213 return err
214 }
215
216
217
218
228 return nil
229 }
230
231 func (tw *Writer) writeGNUHeader(hdr *Header) error {
232
233 const longName = "././@LongLink"
234 if len(hdr.Name) > nameSize {
235 data := hdr.Name + "\x00"
236 if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil {
237 return err
238 }
239 }
240 if len(hdr.Linkname) > nameSize {
241 data := hdr.Linkname + "\x00"
242 if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil {
243 return err
244 }
245 }
246
247
248 var f formatter
249 var spd sparseDatas
250 var spb []byte
251 blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric)
252 if !hdr.AccessTime.IsZero() {
253 f.formatNumeric(blk.toGNU().accessTime(), hdr.AccessTime.Unix())
254 }
255 if !hdr.ChangeTime.IsZero() {
256 f.formatNumeric(blk.toGNU().changeTime(), hdr.ChangeTime.Unix())
257 }
258
259
260
296 blk.setFormat(FormatGNU)
297 if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
298 return err
299 }
300
301
302 if len(spd) > 0 {
303
304 if _, err := tw.w.Write(spb); err != nil {
305 return err
306 }
307 tw.curr = &sparseFileWriter{tw.curr, spd, 0}
308 }
309 return nil
310 }
311
312 type (
313 stringFormatter func([]byte, string)
314 numberFormatter func([]byte, int64)
315 )
316
317
318
319
320
321
322
323 func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block {
324 tw.blk.reset()
325
326 modTime := hdr.ModTime
327 if modTime.IsZero() {
328 modTime = time.Unix(0, 0)
329 }
330
331 v7 := tw.blk.toV7()
332 v7.typeFlag()[0] = hdr.Typeflag
333 fmtStr(v7.name(), hdr.Name)
334 fmtStr(v7.linkName(), hdr.Linkname)
335 fmtNum(v7.mode(), hdr.Mode)
336 fmtNum(v7.uid(), int64(hdr.Uid))
337 fmtNum(v7.gid(), int64(hdr.Gid))
338 fmtNum(v7.size(), hdr.Size)
339 fmtNum(v7.modTime(), modTime.Unix())
340
341 ustar := tw.blk.toUSTAR()
342 fmtStr(ustar.userName(), hdr.Uname)
343 fmtStr(ustar.groupName(), hdr.Gname)
344 fmtNum(ustar.devMajor(), hdr.Devmajor)
345 fmtNum(ustar.devMinor(), hdr.Devminor)
346
347 return &tw.blk
348 }
349
350
351
352
353 func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error {
354 tw.blk.reset()
355
356
357 name = toASCII(name)
358 if len(name) > nameSize {
359 name = name[:nameSize]
360 }
361 name = strings.TrimRight(name, "/")
362
363 var f formatter
364 v7 := tw.blk.toV7()
365 v7.typeFlag()[0] = flag
366 f.formatString(v7.name(), name)
367 f.formatOctal(v7.mode(), 0)
368 f.formatOctal(v7.uid(), 0)
369 f.formatOctal(v7.gid(), 0)
370 f.formatOctal(v7.size(), int64(len(data)))
371 f.formatOctal(v7.modTime(), 0)
372 tw.blk.setFormat(format)
373 if f.err != nil {
374 return f.err
375 }
376
377
378 if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil {
379 return err
380 }
381 _, err := io.WriteString(tw, data)
382 return err
383 }
384
385
386
387
388 func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error {
389 if err := tw.Flush(); err != nil {
390 return err
391 }
392 if _, err := tw.w.Write(blk[:]); err != nil {
393 return err
394 }
395 if isHeaderOnlyType(flag) {
396 size = 0
397 }
398 tw.curr = ®FileWriter{tw.w, size}
399 tw.pad = blockPadding(size)
400 return nil
401 }
402
403
404
405
406 func (tw *Writer) AddFS(fsys fs.FS) error {
407 return fs.WalkDir(fsys, ".", func(name string, d fs.DirEntry, err error) error {
408 if err != nil {
409 return err
410 }
411 if name == "." {
412 return nil
413 }
414 info, err := d.Info()
415 if err != nil {
416 return err
417 }
418 linkTarget := ""
419 if typ := d.Type(); typ == fs.ModeSymlink {
420 var err error
421 linkTarget, err = fs.ReadLink(fsys, name)
422 if err != nil {
423 return err
424 }
425 } else if !typ.IsRegular() && typ != fs.ModeDir {
426 return errors.New("tar: cannot add non-regular file")
427 }
428 h, err := FileInfoHeader(info, linkTarget)
429 if err != nil {
430 return err
431 }
432 h.Name = name
433 if d.IsDir() {
434 h.Name += "/"
435 }
436 if err := tw.WriteHeader(h); err != nil {
437 return err
438 }
439 if !d.Type().IsRegular() {
440 return nil
441 }
442 f, err := fsys.Open(name)
443 if err != nil {
444 return err
445 }
446 defer f.Close()
447 _, err = io.Copy(tw, f)
448 return err
449 })
450 }
451
452
453
454 func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
455 length := len(name)
456 if length <= nameSize || !isASCII(name) {
457 return "", "", false
458 } else if length > prefixSize+1 {
459 length = prefixSize + 1
460 } else if name[length-1] == '/' {
461 length--
462 }
463
464 i := strings.LastIndex(name[:length], "/")
465 nlen := len(name) - i - 1
466 plen := i
467 if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
468 return "", "", false
469 }
470 return name[:i], name[i+1:], true
471 }
472
473
474
475
476
477
478
479
480 func (tw *Writer) Write(b []byte) (int, error) {
481 if tw.err != nil {
482 return 0, tw.err
483 }
484 n, err := tw.curr.Write(b)
485 if err != nil && err != ErrWriteTooLong {
486 tw.err = err
487 }
488 return n, err
489 }
490
491
492
493
494
495
496
497
498
499
500
501 func (tw *Writer) readFrom(r io.Reader) (int64, error) {
502 if tw.err != nil {
503 return 0, tw.err
504 }
505 n, err := tw.curr.ReadFrom(r)
506 if err != nil && err != ErrWriteTooLong {
507 tw.err = err
508 }
509 return n, err
510 }
511
512
513
514
515 func (tw *Writer) Close() error {
516 if tw.err == ErrWriteAfterClose {
517 return nil
518 }
519 if tw.err != nil {
520 return tw.err
521 }
522
523
524 err := tw.Flush()
525 for i := 0; i < 2 && err == nil; i++ {
526 _, err = tw.w.Write(zeroBlock[:])
527 }
528
529
530 tw.err = ErrWriteAfterClose
531 return err
532 }
533
534
535 type regFileWriter struct {
536 w io.Writer
537 nb int64
538 }
539
540 func (fw *regFileWriter) Write(b []byte) (n int, err error) {
541 overwrite := int64(len(b)) > fw.nb
542 if overwrite {
543 b = b[:fw.nb]
544 }
545 if len(b) > 0 {
546 n, err = fw.w.Write(b)
547 fw.nb -= int64(n)
548 }
549 switch {
550 case err != nil:
551 return n, err
552 case overwrite:
553 return n, ErrWriteTooLong
554 default:
555 return n, nil
556 }
557 }
558
559 func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
560 return io.Copy(struct{ io.Writer }{fw}, r)
561 }
562
563
564 func (fw regFileWriter) logicalRemaining() int64 {
565 return fw.nb
566 }
567
568
569 func (fw regFileWriter) physicalRemaining() int64 {
570 return fw.nb
571 }
572
573
574 type sparseFileWriter struct {
575 fw fileWriter
576 sp sparseDatas
577 pos int64
578 }
579
580 func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
581 overwrite := int64(len(b)) > sw.logicalRemaining()
582 if overwrite {
583 b = b[:sw.logicalRemaining()]
584 }
585
586 b0 := b
587 endPos := sw.pos + int64(len(b))
588 for endPos > sw.pos && err == nil {
589 var nf int
590 dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
591 if sw.pos < dataStart {
592 bf := b[:min(int64(len(b)), dataStart-sw.pos)]
593 nf, err = zeroWriter{}.Write(bf)
594 } else {
595 bf := b[:min(int64(len(b)), dataEnd-sw.pos)]
596 nf, err = sw.fw.Write(bf)
597 }
598 b = b[nf:]
599 sw.pos += int64(nf)
600 if sw.pos >= dataEnd && len(sw.sp) > 1 {
601 sw.sp = sw.sp[1:]
602 }
603 }
604
605 n = len(b0) - len(b)
606 switch {
607 case err == ErrWriteTooLong:
608 return n, errMissData
609 case err != nil:
610 return n, err
611 case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
612 return n, errUnrefData
613 case overwrite:
614 return n, ErrWriteTooLong
615 default:
616 return n, nil
617 }
618 }
619
620 func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
621 rs, ok := r.(io.ReadSeeker)
622 if ok {
623 if _, err := rs.Seek(0, io.SeekCurrent); err != nil {
624 ok = false
625 }
626 }
627 if !ok {
628 return io.Copy(struct{ io.Writer }{sw}, r)
629 }
630
631 var readLastByte bool
632 pos0 := sw.pos
633 for sw.logicalRemaining() > 0 && !readLastByte && err == nil {
634 var nf int64
635 dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
636 if sw.pos < dataStart {
637 nf = dataStart - sw.pos
638 if sw.physicalRemaining() == 0 {
639 readLastByte = true
640 nf--
641 }
642 _, err = rs.Seek(nf, io.SeekCurrent)
643 } else {
644 nf = dataEnd - sw.pos
645 nf, err = io.CopyN(sw.fw, rs, nf)
646 }
647 sw.pos += nf
648 if sw.pos >= dataEnd && len(sw.sp) > 1 {
649 sw.sp = sw.sp[1:]
650 }
651 }
652
653
654
655 if readLastByte && err == nil {
656 _, err = mustReadFull(rs, []byte{0})
657 sw.pos++
658 }
659
660 n = sw.pos - pos0
661 switch {
662 case err == io.EOF:
663 return n, io.ErrUnexpectedEOF
664 case err == ErrWriteTooLong:
665 return n, errMissData
666 case err != nil:
667 return n, err
668 case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
669 return n, errUnrefData
670 default:
671 return n, ensureEOF(rs)
672 }
673 }
674
675 func (sw sparseFileWriter) logicalRemaining() int64 {
676 return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
677 }
678
679 func (sw sparseFileWriter) physicalRemaining() int64 {
680 return sw.fw.physicalRemaining()
681 }
682
683
684 type zeroWriter struct{}
685
686 func (zeroWriter) Write(b []byte) (int, error) {
687 for i, c := range b {
688 if c != 0 {
689 return i, errWriteHole
690 }
691 }
692 return len(b), nil
693 }
694
695
696 func ensureEOF(r io.Reader) error {
697 n, err := tryReadFull(r, []byte{0})
698 switch {
699 case n > 0:
700 return ErrWriteTooLong
701 case err == io.EOF:
702 return nil
703 default:
704 return err
705 }
706 }
707
View as plain text