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