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
240
241
242 type (
243 sparseDatas []sparseEntry
244 sparseHoles []sparseEntry
245 )
246
247
248
249 func validateSparseEntries(sp []sparseEntry, size int64) bool {
250
251
252 if size < 0 {
253 return false
254 }
255 var pre sparseEntry
256 for _, cur := range sp {
257 switch {
258 case cur.Offset < 0 || cur.Length < 0:
259 return false
260 case cur.Offset > math.MaxInt64-cur.Length:
261 return false
262 case cur.endOffset() > size:
263 return false
264 case pre.endOffset() > cur.Offset:
265 return false
266 }
267 pre = cur
268 }
269 return true
270 }
271
272
273
274
275
276
277
278
279 func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry {
280 dst := src[:0]
281 for _, s := range src {
282 pos, end := s.Offset, s.endOffset()
283 pos += blockPadding(+pos)
284 if end != size {
285 end -= blockPadding(-end)
286 }
287 if pos < end {
288 dst = append(dst, sparseEntry{Offset: pos, Length: end - pos})
289 }
290 }
291 return dst
292 }
293
294
295
296
297
298
299
300
301
302 func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
303 dst := src[:0]
304 var pre sparseEntry
305 for _, cur := range src {
306 if cur.Length == 0 {
307 continue
308 }
309 pre.Length = cur.Offset - pre.Offset
310 if pre.Length > 0 {
311 dst = append(dst, pre)
312 }
313 pre.Offset = cur.endOffset()
314 }
315 pre.Length = size - pre.Offset
316 return append(dst, pre)
317 }
318
319
320
321
322
323 type fileState interface {
324 logicalRemaining() int64
325 physicalRemaining() int64
326 }
327
328
329
330
331
332
333
334
335
336 func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) {
337 format = FormatUSTAR | FormatPAX | FormatGNU
338 paxHdrs = make(map[string]string)
339
340 var whyNoUSTAR, whyNoPAX, whyNoGNU string
341 var preferPAX bool
342 verifyString := func(s string, size int, name, paxKey string) {
343
344
345
346 tooLong := len(s) > size
347 allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath
348 if hasNUL(s) || (tooLong && !allowLongGNU) {
349 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s)
350 format.mustNotBe(FormatGNU)
351 }
352 if !isASCII(s) || tooLong {
353 canSplitUSTAR := paxKey == paxPath
354 if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
355 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s)
356 format.mustNotBe(FormatUSTAR)
357 }
358 if paxKey == paxNone {
359 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s)
360 format.mustNotBe(FormatPAX)
361 } else {
362 paxHdrs[paxKey] = s
363 }
364 }
365 if v, ok := h.PAXRecords[paxKey]; ok && v == s {
366 paxHdrs[paxKey] = v
367 }
368 }
369 verifyNumeric := func(n int64, size int, name, paxKey string) {
370 if !fitsInBase256(size, n) {
371 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n)
372 format.mustNotBe(FormatGNU)
373 }
374 if !fitsInOctal(size, n) {
375 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n)
376 format.mustNotBe(FormatUSTAR)
377 if paxKey == paxNone {
378 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n)
379 format.mustNotBe(FormatPAX)
380 } else {
381 paxHdrs[paxKey] = strconv.FormatInt(n, 10)
382 }
383 }
384 if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) {
385 paxHdrs[paxKey] = v
386 }
387 }
388 verifyTime := func(ts time.Time, size int, name, paxKey string) {
389 if ts.IsZero() {
390 return
391 }
392 if !fitsInBase256(size, ts.Unix()) {
393 whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts)
394 format.mustNotBe(FormatGNU)
395 }
396 isMtime := paxKey == paxMtime
397 fitsOctal := fitsInOctal(size, ts.Unix())
398 if (isMtime && !fitsOctal) || !isMtime {
399 whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts)
400 format.mustNotBe(FormatUSTAR)
401 }
402 needsNano := ts.Nanosecond() != 0
403 if !isMtime || !fitsOctal || needsNano {
404 preferPAX = true
405 if paxKey == paxNone {
406 whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts)
407 format.mustNotBe(FormatPAX)
408 } else {
409 paxHdrs[paxKey] = formatPAXTime(ts)
410 }
411 }
412 if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) {
413 paxHdrs[paxKey] = v
414 }
415 }
416
417
418 var blk block
419 v7 := blk.toV7()
420 ustar := blk.toUSTAR()
421 gnu := blk.toGNU()
422 verifyString(h.Name, len(v7.name()), "Name", paxPath)
423 verifyString(h.Linkname, len(v7.linkName()), "Linkname", paxLinkpath)
424 verifyString(h.Uname, len(ustar.userName()), "Uname", paxUname)
425 verifyString(h.Gname, len(ustar.groupName()), "Gname", paxGname)
426 verifyNumeric(h.Mode, len(v7.mode()), "Mode", paxNone)
427 verifyNumeric(int64(h.Uid), len(v7.uid()), "Uid", paxUid)
428 verifyNumeric(int64(h.Gid), len(v7.gid()), "Gid", paxGid)
429 verifyNumeric(h.Size, len(v7.size()), "Size", paxSize)
430 verifyNumeric(h.Devmajor, len(ustar.devMajor()), "Devmajor", paxNone)
431 verifyNumeric(h.Devminor, len(ustar.devMinor()), "Devminor", paxNone)
432 verifyTime(h.ModTime, len(v7.modTime()), "ModTime", paxMtime)
433 verifyTime(h.AccessTime, len(gnu.accessTime()), "AccessTime", paxAtime)
434 verifyTime(h.ChangeTime, len(gnu.changeTime()), "ChangeTime", paxCtime)
435
436
437 var whyOnlyPAX, whyOnlyGNU string
438 switch h.Typeflag {
439 case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse:
440
441 if strings.HasSuffix(h.Name, "/") {
442 return FormatUnknown, nil, headerError{"filename may not have trailing slash"}
443 }
444 case TypeXHeader, TypeGNULongName, TypeGNULongLink:
445 return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"}
446 case TypeXGlobalHeader:
447 h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format}
448 if !reflect.DeepEqual(h, h2) {
449 return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"}
450 }
451 whyOnlyPAX = "only PAX supports TypeXGlobalHeader"
452 format.mayOnlyBe(FormatPAX)
453 }
454 if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 {
455 return FormatUnknown, nil, headerError{"negative size on header-only type"}
456 }
457
458
459 if len(h.Xattrs) > 0 {
460 for k, v := range h.Xattrs {
461 paxHdrs[paxSchilyXattr+k] = v
462 }
463 whyOnlyPAX = "only PAX supports Xattrs"
464 format.mayOnlyBe(FormatPAX)
465 }
466 if len(h.PAXRecords) > 0 {
467 for k, v := range h.PAXRecords {
468 switch _, exists := paxHdrs[k]; {
469 case exists:
470 continue
471 case h.Typeflag == TypeXGlobalHeader:
472 paxHdrs[k] = v
473 case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse):
474 paxHdrs[k] = v
475 }
476 }
477 whyOnlyPAX = "only PAX supports PAXRecords"
478 format.mayOnlyBe(FormatPAX)
479 }
480 for k, v := range paxHdrs {
481 if !validPAXRecord(k, v) {
482 return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)}
483 }
484 }
485
486
487
488
508
509
510 if wantFormat := h.Format; wantFormat != FormatUnknown {
511 if wantFormat.has(FormatPAX) && !preferPAX {
512 wantFormat.mayBe(FormatUSTAR)
513 }
514 format.mayOnlyBe(wantFormat)
515 }
516 if format == FormatUnknown {
517 switch h.Format {
518 case FormatUSTAR:
519 err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU}
520 case FormatPAX:
521 err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU}
522 case FormatGNU:
523 err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX}
524 default:
525 err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU}
526 }
527 }
528 return format, paxHdrs, err
529 }
530
531
532 func (h *Header) FileInfo() fs.FileInfo {
533 return headerFileInfo{h}
534 }
535
536
537 type headerFileInfo struct {
538 h *Header
539 }
540
541 func (fi headerFileInfo) Size() int64 { return fi.h.Size }
542 func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
543 func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
544 func (fi headerFileInfo) Sys() any { return fi.h }
545
546
547 func (fi headerFileInfo) Name() string {
548 if fi.IsDir() {
549 return path.Base(path.Clean(fi.h.Name))
550 }
551 return path.Base(fi.h.Name)
552 }
553
554
555 func (fi headerFileInfo) Mode() (mode fs.FileMode) {
556
557 mode = fs.FileMode(fi.h.Mode).Perm()
558
559
560 if fi.h.Mode&c_ISUID != 0 {
561 mode |= fs.ModeSetuid
562 }
563 if fi.h.Mode&c_ISGID != 0 {
564 mode |= fs.ModeSetgid
565 }
566 if fi.h.Mode&c_ISVTX != 0 {
567 mode |= fs.ModeSticky
568 }
569
570
571 switch m := fs.FileMode(fi.h.Mode) &^ 07777; m {
572 case c_ISDIR:
573 mode |= fs.ModeDir
574 case c_ISFIFO:
575 mode |= fs.ModeNamedPipe
576 case c_ISLNK:
577 mode |= fs.ModeSymlink
578 case c_ISBLK:
579 mode |= fs.ModeDevice
580 case c_ISCHR:
581 mode |= fs.ModeDevice
582 mode |= fs.ModeCharDevice
583 case c_ISSOCK:
584 mode |= fs.ModeSocket
585 }
586
587 switch fi.h.Typeflag {
588 case TypeSymlink:
589 mode |= fs.ModeSymlink
590 case TypeChar:
591 mode |= fs.ModeDevice
592 mode |= fs.ModeCharDevice
593 case TypeBlock:
594 mode |= fs.ModeDevice
595 case TypeDir:
596 mode |= fs.ModeDir
597 case TypeFifo:
598 mode |= fs.ModeNamedPipe
599 }
600
601 return mode
602 }
603
604
605 var sysStat func(fi fs.FileInfo, h *Header) error
606
607 const (
608
609
610 c_ISUID = 04000
611 c_ISGID = 02000
612 c_ISVTX = 01000
613
614
615
616 c_ISDIR = 040000
617 c_ISFIFO = 010000
618 c_ISREG = 0100000
619 c_ISLNK = 0120000
620 c_ISBLK = 060000
621 c_ISCHR = 020000
622 c_ISSOCK = 0140000
623 )
624
625
626
627
628
629
630
631
632 func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
633 if fi == nil {
634 return nil, errors.New("archive/tar: FileInfo is nil")
635 }
636 fm := fi.Mode()
637 h := &Header{
638 Name: fi.Name(),
639 ModTime: fi.ModTime(),
640 Mode: int64(fm.Perm()),
641 }
642 switch {
643 case fm.IsRegular():
644 h.Typeflag = TypeReg
645 h.Size = fi.Size()
646 case fi.IsDir():
647 h.Typeflag = TypeDir
648 h.Name += "/"
649 case fm&fs.ModeSymlink != 0:
650 h.Typeflag = TypeSymlink
651 h.Linkname = link
652 case fm&fs.ModeDevice != 0:
653 if fm&fs.ModeCharDevice != 0 {
654 h.Typeflag = TypeChar
655 } else {
656 h.Typeflag = TypeBlock
657 }
658 case fm&fs.ModeNamedPipe != 0:
659 h.Typeflag = TypeFifo
660 case fm&fs.ModeSocket != 0:
661 return nil, fmt.Errorf("archive/tar: sockets not supported")
662 default:
663 return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
664 }
665 if fm&fs.ModeSetuid != 0 {
666 h.Mode |= c_ISUID
667 }
668 if fm&fs.ModeSetgid != 0 {
669 h.Mode |= c_ISGID
670 }
671 if fm&fs.ModeSticky != 0 {
672 h.Mode |= c_ISVTX
673 }
674
675
676 if sys, ok := fi.Sys().(*Header); ok {
677
678
679 h.Uid = sys.Uid
680 h.Gid = sys.Gid
681 h.Uname = sys.Uname
682 h.Gname = sys.Gname
683 h.AccessTime = sys.AccessTime
684 h.ChangeTime = sys.ChangeTime
685 if sys.Xattrs != nil {
686 h.Xattrs = make(map[string]string)
687 for k, v := range sys.Xattrs {
688 h.Xattrs[k] = v
689 }
690 }
691 if sys.Typeflag == TypeLink {
692
693 h.Typeflag = TypeLink
694 h.Size = 0
695 h.Linkname = sys.Linkname
696 }
697 if sys.PAXRecords != nil {
698 h.PAXRecords = make(map[string]string)
699 for k, v := range sys.PAXRecords {
700 h.PAXRecords[k] = v
701 }
702 }
703 }
704 if sysStat != nil {
705 return h, sysStat(fi, h)
706 }
707 return h, nil
708 }
709
710
711
712 func isHeaderOnlyType(flag byte) bool {
713 switch flag {
714 case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
715 return true
716 default:
717 return false
718 }
719 }
720
721 func min(a, b int64) int64 {
722 if a < b {
723 return a
724 }
725 return b
726 }
727
View as plain text