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