Skip to content

Commit

Permalink
archive/tar: support PAX subsecond resolution times
Browse files Browse the repository at this point in the history
Add support for PAX subsecond resolution times. Since the parser
supports negative timestamps, the formatter also handles negative
timestamps.

The relevant PAX specification is:
<<<
Portable file timestamps cannot be negative. If pax encounters a
file with a negative timestamp in copy or write mode, it can reject
the file, substitute a non-negative timestamp, or generate a
non-portable timestamp with a leading '-'.
>>>

<<<
All of these time records shall be formatted as a decimal
representation of the time in seconds since the Epoch.
If a <period> ( '.' ) decimal point character is present,
the digits to the right of the point shall represent the units of
a subsecond timing granularity, where the first digit is tenths of
a second and each subsequent digit is a tenth of the previous digit.
>>>

Fixes #11171

Change-Id: Ied108f3d2654390bc1b0ddd66a4081c2b83e490b
Reviewed-on: https://go-review.googlesource.com/55552
Run-TryBot: Joe Tsai <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
  • Loading branch information
dsnet committed Aug 15, 2017
1 parent fcf445d commit 2bcc24e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 11 deletions.
3 changes: 1 addition & 2 deletions src/archive/tar/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ func (h *Header) allowedFormats() (format int, paxHdrs map[string]string) {
if paxKey == paxNone {
format &^= formatPAX // No PAX
} else {
// TODO(dsnet): Support PAX time here.
// paxHdrs[paxKey] = formatPAXTime(ts)
paxHdrs[paxKey] = formatPAXTime(ts)
}
}
}
Expand Down
19 changes: 18 additions & 1 deletion src/archive/tar/strconv.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package tar

import (
"bytes"
"fmt"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -218,7 +219,23 @@ func parsePAXTime(s string) (time.Time, error) {
return time.Unix(secs, int64(nsecs)), nil
}

// TODO(dsnet): Implement formatPAXTime.
// formatPAXTime converts ts into a time of the form %d.%d as described in the
// PAX specification. This function is capable of negative timestamps.
func formatPAXTime(ts time.Time) (s string) {
secs, nsecs := ts.Unix(), ts.Nanosecond()
if nsecs == 0 {
return strconv.FormatInt(secs, 10)
}

// If seconds is negative, then perform correction.
sign := ""
if secs < 0 {
sign = "-" // Remember sign
secs = -(secs + 1) // Add a second to secs
nsecs = -(nsecs - 1E9) // Take that second away from nsecs
}
return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
}

// parsePAXRecord parses the input PAX record string into a key-value pair.
// If parsing is successful, it will slice off the currently read record and
Expand Down
45 changes: 45 additions & 0 deletions src/archive/tar/strconv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,51 @@ func TestParsePAXTime(t *testing.T) {
}
}

func TestFormatPAXTime(t *testing.T) {
vectors := []struct {
sec, nsec int64
want string
}{
{1350244992, 0, "1350244992"},
{1350244992, 300000000, "1350244992.3"},
{1350244992, 23960100, "1350244992.0239601"},
{1350244992, 23960108, "1350244992.023960108"},
{+1, +1E9 - 1E0, "1.999999999"},
{+1, +1E9 - 1E3, "1.999999"},
{+1, +1E9 - 1E6, "1.999"},
{+1, +0E0 - 0E0, "1"},
{+1, +1E6 - 0E0, "1.001"},
{+1, +1E3 - 0E0, "1.000001"},
{+1, +1E0 - 0E0, "1.000000001"},
{0, 1E9 - 1E0, "0.999999999"},
{0, 1E9 - 1E3, "0.999999"},
{0, 1E9 - 1E6, "0.999"},
{0, 0E0, "0"},
{0, 1E6 + 0E0, "0.001"},
{0, 1E3 + 0E0, "0.000001"},
{0, 1E0 + 0E0, "0.000000001"},
{-1, -1E9 + 1E0, "-1.999999999"},
{-1, -1E9 + 1E3, "-1.999999"},
{-1, -1E9 + 1E6, "-1.999"},
{-1, -0E0 + 0E0, "-1"},
{-1, -1E6 + 0E0, "-1.001"},
{-1, -1E3 + 0E0, "-1.000001"},
{-1, -1E0 + 0E0, "-1.000000001"},
{-1350244992, 0, "-1350244992"},
{-1350244992, -300000000, "-1350244992.3"},
{-1350244992, -23960100, "-1350244992.0239601"},
{-1350244992, -23960108, "-1350244992.023960108"},
}

for _, v := range vectors {
got := formatPAXTime(time.Unix(v.sec, v.nsec))
if got != v.want {
t.Errorf("formatPAXTime(%ds, %dns): got %q, want %q",
v.sec, v.nsec, got, v.want)
}
}
}

func TestParsePAXRecord(t *testing.T) {
medName := strings.Repeat("CD", 50)
longName := strings.Repeat("AB", 100)
Expand Down
12 changes: 4 additions & 8 deletions src/archive/tar/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,13 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
return err
}

// TODO(dsnet): Add PAX timestamps with nanosecond support.
hdrCpy := *hdr
hdrCpy.ModTime = hdrCpy.ModTime.Truncate(time.Second)

switch allowedFormats, paxHdrs := hdrCpy.allowedFormats(); {
switch allowedFormats, paxHdrs := hdr.allowedFormats(); {
case allowedFormats&formatUSTAR != 0:
return tw.writeUSTARHeader(&hdrCpy)
return tw.writeUSTARHeader(hdr)
case allowedFormats&formatPAX != 0:
return tw.writePAXHeader(&hdrCpy, paxHdrs)
return tw.writePAXHeader(hdr, paxHdrs)
case allowedFormats&formatGNU != 0:
return tw.writeGNUHeader(&hdrCpy)
return tw.writeGNUHeader(hdr)
default:
return ErrHeader
}
Expand Down

0 comments on commit 2bcc24e

Please sign in to comment.