Commit 4c557743 authored by Joe Tsai's avatar Joe Tsai Committed by Joe Tsai

archive/tar: re-implement USTAR path splitting

The logic for USTAR was disabled because a previous implementation of
Writer had a wrong understanding of the differences between USTAR and GNU,
causing the prefix field is incorrectly be populated in GNU files.

Now that this issue has been fixed, we can re-enable the logic for USTAR
path splitting, which allows Writer to use the USTAR for a wider range
of possible inputs.

Updates #9683
Updates #12594
Updates #17630

Change-Id: I9fe34e5df63f99c6dd56fee3a7e7e4d6ec3995c9
Reviewed-on: https://go-review.googlesource.com/55574
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent a0237c52
...@@ -86,6 +86,7 @@ func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) { ...@@ -86,6 +86,7 @@ func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) {
paxHdrs = make(map[string]string) paxHdrs = make(map[string]string)
verifyString := func(s string, size int, gnuLong bool, paxKey string) { verifyString := func(s string, size int, gnuLong bool, paxKey string) {
// NUL-terminator is optional for path and linkpath. // NUL-terminator is optional for path and linkpath.
// Technically, it is required for uname and gname, // Technically, it is required for uname and gname,
// but neither GNU nor BSD tar checks for it. // but neither GNU nor BSD tar checks for it.
...@@ -95,9 +96,10 @@ func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) { ...@@ -95,9 +96,10 @@ func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) {
format &^= formatGNU // No GNU format &^= formatGNU // No GNU
} }
if !isASCII(s) || tooLong { if !isASCII(s) || tooLong {
// TODO(dsnet): If the path is splittable, it is possible to still canSplitUSTAR := paxKey == paxPath
// use the USTAR format. if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok {
format &^= formatUSTAR // No USTAR format &^= formatUSTAR // No USTAR
}
if paxKey == paxNone { if paxKey == paxNone {
format &^= formatPAX // No PAX format &^= formatPAX // No PAX
} else { } else {
......
...@@ -28,6 +28,7 @@ type Writer struct { ...@@ -28,6 +28,7 @@ type Writer struct {
pad int64 // amount of padding to write after current file entry pad int64 // amount of padding to write after current file entry
closed bool closed bool
hdr Header // Shallow copy of Header that is safe for mutations
blk block // Buffer to use as temporary local storage blk block // Buffer to use as temporary local storage
} }
...@@ -59,25 +60,30 @@ func (tw *Writer) WriteHeader(hdr *Header) error { ...@@ -59,25 +60,30 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
return err return err
} }
switch allowedFormats, paxHdrs := hdr.allowedFormats(); { tw.hdr = *hdr // Shallow copy of Header
switch allowedFormats, paxHdrs := tw.hdr.allowedFormats(); {
case allowedFormats&formatUSTAR != 0: case allowedFormats&formatUSTAR != 0:
return tw.writeUSTARHeader(hdr) return tw.writeUSTARHeader(&tw.hdr)
case allowedFormats&formatPAX != 0: case allowedFormats&formatPAX != 0:
return tw.writePAXHeader(hdr, paxHdrs) return tw.writePAXHeader(&tw.hdr, paxHdrs)
case allowedFormats&formatGNU != 0: case allowedFormats&formatGNU != 0:
return tw.writeGNUHeader(hdr) return tw.writeGNUHeader(&tw.hdr)
default: default:
return ErrHeader return ErrHeader
} }
} }
func (tw *Writer) writeUSTARHeader(hdr *Header) error { func (tw *Writer) writeUSTARHeader(hdr *Header) error {
// TODO(dsnet): Support USTAR prefix/suffix path splitting. // Check if we can use USTAR prefix/suffix splitting.
// See https://golang.org/issue/12594 var namePrefix string
if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok {
namePrefix, hdr.Name = prefix, suffix
}
// Pack the main header. // Pack the main header.
var f formatter var f formatter
blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
f.formatString(blk.USTAR().Prefix(), namePrefix)
blk.SetFormat(formatUSTAR) blk.SetFormat(formatUSTAR)
if f.err != nil { if f.err != nil {
return f.err // Should never happen since header is validated return f.err // Should never happen since header is validated
......
...@@ -151,15 +151,9 @@ func TestWriter(t *testing.T) { ...@@ -151,15 +151,9 @@ func TestWriter(t *testing.T) {
contents: strings.Repeat("\x00", 4<<10), contents: strings.Repeat("\x00", 4<<10),
}}, }},
}, { }, {
// TODO(dsnet): The Writer output should match the following file. // This file was produced using GNU tar v1.17.
// To fix an issue (see https://golang.org/issue/12594), we disabled
// prefix support, which alters the generated output.
/*
// This file was produced using gnu tar 1.17
// gnutar -b 4 --format=ustar (longname/)*15 + file.txt // gnutar -b 4 --format=ustar (longname/)*15 + file.txt
file: "testdata/ustar.tar" file: "testdata/ustar.tar",
*/
file: "testdata/ustar.issue12594.tar", // This is a valid tar file, but not expected
entries: []*entry{{ entries: []*entry{{
header: &Header{ header: &Header{
Name: strings.Repeat("longname/", 15) + "file.txt", Name: strings.Repeat("longname/", 15) + "file.txt",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment