1
2
3
4
5
6
7
8
9
10
11 package tar
12
13 import (
14 "errors"
15 "fmt"
16 "internal/godebug"
17 "io/fs"
18 "math"
19 "path"
20 "reflect"
21 "strconv"
22 "strings"
23 "time"
24 )
25
26
27
28
29
30 var tarinsecurepath = godebug.New("tarinsecurepath")
31
32 var (
33 ErrHeader = errors.New("archive/tar: invalid tar header")
34 ErrWriteTooLong = errors.New("archive/tar: write too long")
35 ErrFieldTooLong = errors.New("archive/tar: header field too long")
36 ErrWriteAfterClose = errors.New("archive/tar: write after close")
37 ErrInsecurePath = errors.New("archive/tar: insecure file path")
38 errMissData = errors.New("archive/tar: sparse file references non-existent data")
39 errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data")
40 errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole")
41 )
42
43 type headerError []string
44
45 func (he headerError) Error() string {
46 const prefix = "archive/tar: cannot encode header"
47 var ss []string
48 for _, s := range he {
49 if s != "" {
50 ss = append(ss, s)
51 }
52 }
53 if len(ss) == 0 {
54 return prefix
55 }
56 return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and "))
57 }
58
59
60 const (
61
62 TypeReg = '0'
63
64
65 TypeRegA = '\x00'
66
67
68 TypeLink = '1'
69 TypeSymlink = '2'
70 TypeChar = '3'
71 TypeBlock = '4'
72 TypeDir = '5'
73 TypeFifo = '6'
74
75
76 TypeCont = '7'
77
78
79
80
81 TypeXHeader = 'x'
82
83
84
85
86
87 TypeXGlobalHeader = 'g'
88
89
90 TypeGNUSparse = 'S'
91
92
93
94
95 TypeGNULongName = 'L'
96 TypeGNULongLink = 'K'
97 )
98
99
100 const (
101 paxNone = ""
102 paxPath = "path"
103 paxLinkpath = "linkpath"
104 paxSize = "size"
105 paxUid = "uid"
106 paxGid = "gid"
107 paxUname = "uname"
108 paxGname = "gname"
109 paxMtime = "mtime"
110 paxAtime = "atime"
111 paxCtime = "ctime"
112 paxCharset = "charset"
113 paxComment = "comment"
114
115 paxSchilyXattr = "SCHILY.xattr."
116
117
118 paxGNUSparse = "GNU.sparse."
119 paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
120 paxGNUSparseOffset = "GNU.sparse.offset"
121 paxGNUSparseNumBytes = "GNU.sparse.numbytes"
122 paxGNUSparseMap = "GNU.sparse.map"
123 paxGNUSparseName = "GNU.sparse.name"
124 paxGNUSparseMajor = "GNU.sparse.major"
125 paxGNUSparseMinor = "GNU.sparse.minor"
126 paxGNUSparseSize = "GNU.sparse.size"
127 paxGNUSparseRealSize = "GNU.sparse.realsize"
128 )
129
130
131
132
133
134 var basicKeys = map[string]bool{
135 paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true,
136 paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true,
137 }
138
139
140
141
142
143
144
145
146 type Header struct {
147
148
149
150 Typeflag byte
151
152 Name string
153 Linkname string
154
155 Size int64
156 Mode int64
157 Uid int
158 Gid int
159 Uname string
160 Gname string
161
162
163
164
165
166
167 ModTime time.Time
168 AccessTime time.Time
169 ChangeTime time.Time
170
171 Devmajor int64
172 Devminor int64
173
174
175
176
177
178
179
180
181
182
183
184
185 Xattrs map[string]string
186
187
188
189
190
191
192
193
194
195
196
197 PAXRecords map[string]string
198
199
200
201
202
203
204
205
206
207
208 Format Format
209 }
210
211
212 type sparseEntry struct{ Offset, Length int64 }
213
214 func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length }
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 type (
249 sparseDatas []sparseEntry
250 sparseHoles []sparseEntry
251 )
252
253
254
255 func validateSparseEntries(sp []sparseEntry, size int64) bool {
256
257
258 if size < 0 {
259 return false
260 }
261 var pre sparseEntry
262 for _, cur := range sp {
263 switch {
264 case cur.Offset < 0 || cur.Length < 0:
265 return false
266 case cur.Offset > math.MaxInt64-cur.Length:
267 return false
268 case cur.endOffset() > size:
269 return false
270 case pre.endOffset() > cur.Offset:
271 return false
272 }
273 pre = cur
274 }
275 return true
276 }
277
278
279
280
281
282
283
284
285 func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry {
286 dst := src[:0]
287 for _, s := range src {
288 pos, end := s.Offset, s.endOffset()
289 pos += blockPadding(+pos)
290 if end != size {
291 end -= blockPadding(-end)
292 }
293 if pos < end {
294 dst = append(dst, sparseEntry{Offset: pos, Length: end - pos})
295 }
296 }
297 return dst
298 }
299
300
301
302
303
304
305
306
307
308 func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
309 dst := src[:0]
310 var pre sparseEntry
311 for _, cur := range src {
312 if cur.Length == 0 {
313 continue
314 }
315 pre.Length = cur.Offset - pre.Offset
316 if pre.Length > 0 {
317 dst = append(dst, pre)
318 }
319 pre.Offset = cur.endOffset()
320 }
321 pre.Length = size - pre.Offset
322 return append(dst, pre)
323 }
324
325
326
327
328
329 type fileState interface {
330 logicalRemaining() int64
331 physicalRemaining() int64
332 }
333
334
335
336
337
338
339
340
341
342 func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) {
343 format = FormatUSTAR | FormatPAX | FormatGNU
344 paxHdrs = make(map[string]string)
345
346 var whyNoUSTAR, whyNoPAX, whyNoGNU string
347 var preferPAX bool
348 verifyString := func(s string, size int, name, paxKey string) {
349
350
351
352 tooLong := len(s) > size
353 allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
354 if hasNUL(s) || (tooLong && !allowLongGNU) {
355 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s)
356 format.mustNotBe(FormatGNU)
357 }
358 if !isASCII(s) || tooLong {
359 canSplitUSTAR := paxKey == paxPath
360 if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
361 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s)
362 format.mustNotBe(FormatUSTAR)
363 }
364 if paxKey == paxNone {
365 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s)
366 format.mustNotBe(FormatPAX)
367 } else {
368 paxHdrs[paxKey] = s
369 }
370 }
371 if v, ok := h.PAXRecords[paxKey]; ok && v == s {
372 paxHdrs[paxKey] = v
373 }
374 }
375 verifyNumeric := func(n int64, size int, name, paxKey string) {
376 if !fitsInBase256(size, n) {
377 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n)
378 format.mustNotBe(FormatGNU)
379 }
380 if !fitsInOctal(size, n) {
381 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n)
382 format.mustNotBe(FormatUSTAR)
383 if paxKey == paxNone {
384 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n)
385 format.mustNotBe(FormatPAX)
386 } else {
387 paxHdrs[paxKey] = strconv.FormatInt(n, 10)
388 }
389 }
390 if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) {
391 paxHdrs[paxKey] = v
392 }
393 }
394 verifyTime := func(ts time.Time, size int, name, paxKey string) {
395 if ts.IsZero() {
396 return
397 }
398 if !fitsInBase256(size, ts.Unix()) {
399 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts)
400 format.mustNotBe(FormatGNU)
401 }
402 isMtime := paxKey == paxMtime
403 fitsOctal := fitsInOctal(size, ts.Unix())
404 if (isMtime && !fitsOctal) || !isMtime {
405 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts)
406 format.mustNotBe(FormatUSTAR)
407 }
408 needsNano := ts.Nanosecond() != 0
409 if !isMtime || !fitsOctal || needsNano {
410 preferPAX = true
411 if paxKey == paxNone {
412 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts)
413 format.mustNotBe(FormatPAX)
414 } else {
415 paxHdrs[paxKey] = formatPAXTime(ts)
416 }
417 }
418 if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) {
419 paxHdrs[paxKey] = v
420 }
421 }
422
423
424 var blk block
425 v7 := blk.toV7()
426 ustar := blk.toUSTAR()
427 gnu := blk.toGNU()
428 verifyString(h.Name, len(v7.name()), "Name", paxPath)
429 verifyString(h.Linkname, len(v7.linkName()), "Linkname", paxLinkpath)
430 verifyString(h.Uname, len(ustar.userName()), "Uname", paxUname)
431 verifyString(h.Gname, len(ustar.groupName()), "Gname", paxGname)
432 verifyNumeric(h.Mode, len(v7.mode()), "Mode", paxNone)
433 verifyNumeric(int64(h.Uid), len(v7.uid()), "Uid", paxUid)
434 verifyNumeric(int64(h.Gid), len(v7.gid()), "Gid", paxGid)
435 verifyNumeric(h.Size, len(v7.size()), "Size", paxSize)
436 verifyNumeric(h.Devmajor, len(ustar.devMajor()), "Devmajor", paxNone)
437 verifyNumeric(h.Devminor, len(ustar.devMinor()), "Devminor", paxNone)
438 verifyTime(h.ModTime, len(v7.modTime()), "ModTime", paxMtime)
439 verifyTime(h.AccessTime, len(gnu.accessTime()), "AccessTime", paxAtime)
440 verifyTime(h.ChangeTime, len(gnu.changeTime()), "ChangeTime", paxCtime)
441
442
443 var whyOnlyPAX, whyOnlyGNU string
444 switch h.Typeflag {
445 case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse:
446
447 if strings.HasSuffix(h.Name, "/") {
448 return FormatUnknown, nil, headerError{"filename may not have trailing slash"}
449 }
450 case TypeXHeader, TypeGNULongName, TypeGNULongLink:
451 return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"}
452 case TypeXGlobalHeader:
453 h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}
454 if !reflect.DeepEqual(h, h2) {
455 return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"}
456 }
457 whyOnlyPAX = "only PAX supports TypeXGlobalHeader"
458 format.mayOnlyBe(FormatPAX)
459 }
460 if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
461 return FormatUnknown, nil, headerError{"negative size on header-only type"}
462 }
463
464
465 if len(h.Xattrs) > 0 {
466 for k, v := range h.Xattrs {
467 paxHdrs[paxSchilyXattr+k] = v
468 }
469 whyOnlyPAX = "only PAX supports Xattrs"
470 format.mayOnlyBe(FormatPAX)
471 }
472 if len(h.PAXRecords) > 0 {
473 for k, v := range h.PAXRecords {
474 switch _, exists := paxHdrs[k]; {
475 case exists:
476 continue
477 case h.Typeflag == TypeXGlobalHeader:
478 paxHdrs[k] = v
479 case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse):
480 paxHdrs[k] = v
481 }
482 }
483 whyOnlyPAX = "only PAX supports PAXRecords"
484 format.mayOnlyBe(FormatPAX)
485 }
486 for k, v := range paxHdrs {
487 if !validPAXRecord(k, v) {
488 return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)}
489 }
490 }
491
492
493
494
514
515
516 if wantFormat := h.Format; wantFormat != FormatUnknown {
517 if wantFormat.has(FormatPAX) && !preferPAX {
518 wantFormat.mayBe(FormatUSTAR)
519 }
520 format.mayOnlyBe(wantFormat)
521 }
522 if format == FormatUnknown {
523 switch h.Format {
524 case FormatUSTAR:
525 err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU}
526 case FormatPAX:
527 err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU}
528 case FormatGNU:
529 err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX}
530 default:
531 err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU}
532 }
533 }
534 return format, paxHdrs, err
535 }
536
537
538 func (h *Header) FileInfo() fs.FileInfo {
539 return headerFileInfo{h}
540 }
541
542
543 type headerFileInfo struct {
544 h *Header
545 }
546
547 func (fi headerFileInfo) Size() int64 { return fi.h.Size }
548 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
549 func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
550 func (fi headerFileInfo) Sys() any { return fi.h }
551
552
553 func (fi headerFileInfo) Name() string {
554 if fi.IsDir() {
555 return path.Base(path.Clean(fi.h.Name))
556 }
557 return path.Base(fi.h.Name)
558 }
559
560
561 func (fi headerFileInfo) Mode() (mode fs.FileMode) {
562
563 mode = fs.FileMode(fi.h.Mode).Perm()
564
565
566 if fi.h.Mode&c_ISUID != 0 {
567 mode |= fs.ModeSetuid
568 }
569 if fi.h.Mode&c_ISGID != 0 {
570 mode |= fs.ModeSetgid
571 }
572 if fi.h.Mode&c_ISVTX != 0 {
573 mode |= fs.ModeSticky
574 }
575
576
577 switch m := fs.FileMode(fi.h.Mode) &^ 07777; m {
578 case c_ISDIR:
579 mode |= fs.ModeDir
580 case c_ISFIFO:
581 mode |= fs.ModeNamedPipe
582 case c_ISLNK:
583 mode |= fs.ModeSymlink
584 case c_ISBLK:
585 mode |= fs.ModeDevice
586 case c_ISCHR:
587 mode |= fs.ModeDevice
588 mode |= fs.ModeCharDevice
589 case c_ISSOCK:
590 mode |= fs.ModeSocket
591 }
592
593 switch fi.h.Typeflag {
594 case TypeSymlink:
595 mode |= fs.ModeSymlink
596 case TypeChar:
597 mode |= fs.ModeDevice
598 mode |= fs.ModeCharDevice
599 case TypeBlock:
600 mode |= fs.ModeDevice
601 case TypeDir:
602 mode |= fs.ModeDir
603 case TypeFifo:
604 mode |= fs.ModeNamedPipe
605 }
606
607 return mode
608 }
609
610 func (fi headerFileInfo) String() string {
611 return fs.FormatFileInfo(fi)
612 }
613
614
615 var sysStat func(fi fs.FileInfo, h *Header, doNameLookups bool) error
616
617 const (
618
619
620 c_ISUID = 04000
621 c_ISGID = 02000
622 c_ISVTX = 01000
623
624
625
626 c_ISDIR = 040000
627 c_ISFIFO = 010000
628 c_ISREG = 0100000
629 c_ISLNK = 0120000
630 c_ISBLK = 060000
631 c_ISCHR = 020000
632 c_ISSOCK = 0140000
633 )
634
635
636
637
638
639
640
641
642
643
644
645
646 func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
647 if fi == nil {
648 return nil, errors.New("archive/tar: FileInfo is nil")
649 }
650 fm := fi.Mode()
651 h := &Header{
652 Name: fi.Name(),
653 ModTime: fi.ModTime(),
654 Mode: int64(fm.Perm()),
655 }
656 switch {
657 case fm.IsRegular():
658 h.Typeflag = TypeReg
659 h.Size = fi.Size()
660 case fi.IsDir():
661 h.Typeflag = TypeDir
662 h.Name += "/"
663 case fm&fs.ModeSymlink != 0:
664 h.Typeflag = TypeSymlink
665 h.Linkname = link
666 case fm&fs.ModeDevice != 0:
667 if fm&fs.ModeCharDevice != 0 {
668 h.Typeflag = TypeChar
669 } else {
670 h.Typeflag = TypeBlock
671 }
672 case fm&fs.ModeNamedPipe != 0:
673 h.Typeflag = TypeFifo
674 case fm&fs.ModeSocket != 0:
675 return nil, fmt.Errorf("archive/tar: sockets not supported")
676 default:
677 return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
678 }
679 if fm&fs.ModeSetuid != 0 {
680 h.Mode |= c_ISUID
681 }
682 if fm&fs.ModeSetgid != 0 {
683 h.Mode |= c_ISGID
684 }
685 if fm&fs.ModeSticky != 0 {
686 h.Mode |= c_ISVTX
687 }
688
689
690 if sys, ok := fi.Sys().(*Header); ok {
691
692
693 h.Uid = sys.Uid
694 h.Gid = sys.Gid
695 h.Uname = sys.Uname
696 h.Gname = sys.Gname
697 h.AccessTime = sys.AccessTime
698 h.ChangeTime = sys.ChangeTime
699 if sys.Xattrs != nil {
700 h.Xattrs = make(map[string]string)
701 for k, v := range sys.Xattrs {
702 h.Xattrs[k] = v
703 }
704 }
705 if sys.Typeflag == TypeLink {
706
707 h.Typeflag = TypeLink
708 h.Size = 0
709 h.Linkname = sys.Linkname
710 }
711 if sys.PAXRecords != nil {
712 h.PAXRecords = make(map[string]string)
713 for k, v := range sys.PAXRecords {
714 h.PAXRecords[k] = v
715 }
716 }
717 }
718 var doNameLookups = true
719 if iface, ok := fi.(FileInfoNames); ok {
720 doNameLookups = false
721 var err error
722 h.Gname, err = iface.Gname()
723 if err != nil {
724 return nil, err
725 }
726 h.Uname, err = iface.Uname()
727 if err != nil {
728 return nil, err
729 }
730 }
731 if sysStat != nil {
732 return h, sysStat(fi, h, doNameLookups)
733 }
734 return h, nil
735 }
736
737
738
739
740 type FileInfoNames interface {
741 fs.FileInfo
742
743 Uname() (string, error)
744
745 Gname() (string, error)
746 }
747
748
749
750 func isHeaderOnlyType(flag byte) bool {
751 switch flag {
752 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
753 return true
754 default:
755 return false
756 }
757 }
758
View as plain text