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
419 if !d.IsDir() && !info.Mode().IsRegular() {
420 return errors.New("tar: cannot add non-regular file")
421 }
422 h, err := FileInfoHeader(info, "")
423 if err != nil {
424 return err
425 }
426 h.Name = name
427 if err := tw.WriteHeader(h); err != nil {
428 return err
429 }
430 if d.IsDir() {
431 return nil
432 }
433 f, err := fsys.Open(name)
434 if err != nil {
435 return err
436 }
437 defer f.Close()
438 _, err = io.Copy(tw, f)
439 return err
440 })
441 }
442
443
444
445 func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
446 length := len(name)
447 if length <= nameSize || !isASCII(name) {
448 return "", "", false
449 } else if length > prefixSize+1 {
450 length = prefixSize + 1
451 } else if name[length-1] == '/' {
452 length--
453 }
454
455 i := strings.LastIndex(name[:length], "/")
456 nlen := len(name) - i - 1
457 plen := i
458 if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
459 return "", "", false
460 }
461 return name[:i], name[i+1:], true
462 }
463
464
465
466
467
468
469
470
471 func (tw *Writer) Write(b []byte) (int, error) {
472 if tw.err != nil {
473 return 0, tw.err
474 }
475 n, err := tw.curr.Write(b)
476 if err != nil && err != ErrWriteTooLong {
477 tw.err = err
478 }
479 return n, err
480 }
481
482
483
484
485
486
487
488
489
490
491
492 func (tw *Writer) readFrom(r io.Reader) (int64, error) {
493 if tw.err != nil {
494 return 0, tw.err
495 }
496 n, err := tw.curr.ReadFrom(r)
497 if err != nil && err != ErrWriteTooLong {
498 tw.err = err
499 }
500 return n, err
501 }
502
503
504
505
506 func (tw *Writer) Close() error {
507 if tw.err == ErrWriteAfterClose {
508 return nil
509 }
510 if tw.err != nil {
511 return tw.err
512 }
513
514
515 err := tw.Flush()
516 for i := 0; i < 2 && err == nil; i++ {
517 _, err = tw.w.Write(zeroBlock[:])
518 }
519
520
521 tw.err = ErrWriteAfterClose
522 return err
523 }
524
525
526 type regFileWriter struct {
527 w io.Writer
528 nb int64
529 }
530
531 func (fw *regFileWriter) Write(b []byte) (n int, err error) {
532 overwrite := int64(len(b)) > fw.nb
533 if overwrite {
534 b = b[:fw.nb]
535 }
536 if len(b) > 0 {
537 n, err = fw.w.Write(b)
538 fw.nb -= int64(n)
539 }
540 switch {
541 case err != nil:
542 return n, err
543 case overwrite:
544 return n, ErrWriteTooLong
545 default:
546 return n, nil
547 }
548 }
549
550 func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
551 return io.Copy(struct{ io.Writer }{fw}, r)
552 }
553
554
555 func (fw regFileWriter) logicalRemaining() int64 {
556 return fw.nb
557 }
558
559
560 func (fw regFileWriter) physicalRemaining() int64 {
561 return fw.nb
562 }
563
564
565 type sparseFileWriter struct {
566 fw fileWriter
567 sp sparseDatas
568 pos int64
569 }
570
571 func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
572 overwrite := int64(len(b)) > sw.logicalRemaining()
573 if overwrite {
574 b = b[:sw.logicalRemaining()]
575 }
576
577 b0 := b
578 endPos := sw.pos + int64(len(b))
579 for endPos > sw.pos && err == nil {
580 var nf int
581 dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
582 if sw.pos < dataStart {
583 bf := b[:min(int64(len(b)), dataStart-sw.pos)]
584 nf, err = zeroWriter{}.Write(bf)
585 } else {
586 bf := b[:min(int64(len(b)), dataEnd-sw.pos)]
587 nf, err = sw.fw.Write(bf)
588 }
589 b = b[nf:]
590 sw.pos += int64(nf)
591 if sw.pos >= dataEnd && len(sw.sp) > 1 {
592 sw.sp = sw.sp[1:]
593 }
594 }
595
596 n = len(b0) - len(b)
597 switch {
598 case err == ErrWriteTooLong:
599 return n, errMissData
600 case err != nil:
601 return n, err
602 case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
603 return n, errUnrefData
604 case overwrite:
605 return n, ErrWriteTooLong
606 default:
607 return n, nil
608 }
609 }
610
611 func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
612 rs, ok := r.(io.ReadSeeker)
613 if ok {
614 if _, err := rs.Seek(0, io.SeekCurrent); err != nil {
615 ok = false
616 }
617 }
618 if !ok {
619 return io.Copy(struct{ io.Writer }{sw}, r)
620 }
621
622 var readLastByte bool
623 pos0 := sw.pos
624 for sw.logicalRemaining() > 0 && !readLastByte && err == nil {
625 var nf int64
626 dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
627 if sw.pos < dataStart {
628 nf = dataStart - sw.pos
629 if sw.physicalRemaining() == 0 {
630 readLastByte = true
631 nf--
632 }
633 _, err = rs.Seek(nf, io.SeekCurrent)
634 } else {
635 nf = dataEnd - sw.pos
636 nf, err = io.CopyN(sw.fw, rs, nf)
637 }
638 sw.pos += nf
639 if sw.pos >= dataEnd && len(sw.sp) > 1 {
640 sw.sp = sw.sp[1:]
641 }
642 }
643
644
645
646 if readLastByte && err == nil {
647 _, err = mustReadFull(rs, []byte{0})
648 sw.pos++
649 }
650
651 n = sw.pos - pos0
652 switch {
653 case err == io.EOF:
654 return n, io.ErrUnexpectedEOF
655 case err == ErrWriteTooLong:
656 return n, errMissData
657 case err != nil:
658 return n, err
659 case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
660 return n, errUnrefData
661 default:
662 return n, ensureEOF(rs)
663 }
664 }
665
666 func (sw sparseFileWriter) logicalRemaining() int64 {
667 return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
668 }
669
670 func (sw sparseFileWriter) physicalRemaining() int64 {
671 return sw.fw.physicalRemaining()
672 }
673
674
675 type zeroWriter struct{}
676
677 func (zeroWriter) Write(b []byte) (int, error) {
678 for i, c := range b {
679 if c != 0 {
680 return i, errWriteHole
681 }
682 }
683 return len(b), nil
684 }
685
686
687 func ensureEOF(r io.Reader) error {
688 n, err := tryReadFull(r, []byte{0})
689 switch {
690 case n > 0:
691 return ErrWriteTooLong
692 case err == io.EOF:
693 return nil
694 default:
695 return err
696 }
697 }
698
View as plain text