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