// Copyright 2016 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package tarimport ()// hasNUL reports whether the NUL character exists within s.func hasNUL( string) bool {returnstrings.Contains(, "\x00")}// isASCII reports whether the input is an ASCII C-style string.func isASCII( string) bool {for , := range {if >= 0x80 || == 0x00 {returnfalse } }returntrue}// toASCII converts the input to an ASCII C-style string.// This is a best effort conversion, so invalid characters are dropped.func toASCII( string) string {ifisASCII() {return } := make([]byte, 0, len())for , := range {if < 0x80 && != 0x00 { = append(, byte()) } }returnstring()}type parser struct { err error// Last error seen}type formatter struct { err error// Last error seen}// parseString parses bytes as a NUL-terminated C-style string.// If a NUL byte is not found then the whole slice is returned as a string.func (*parser) ( []byte) string {if := bytes.IndexByte(, 0); >= 0 {returnstring([:]) }returnstring()}// formatString copies s into b, NUL-terminating if possible.func ( *formatter) ( []byte, string) {iflen() > len() { .err = ErrFieldTooLong }copy(, )iflen() < len() { [len()] = 0 }// Some buggy readers treat regular files with a trailing slash // in the V7 path field as a directory even though the full path // recorded elsewhere (e.g., via PAX record) contains no trailing slash.iflen() > len() && [len()-1] == '/' { := len(strings.TrimRight([:len()-1], "/")) [] = 0// Replace trailing slash with NUL terminator }}// fitsInBase256 reports whether x can be encoded into n bytes using base-256// encoding. Unlike octal encoding, base-256 encoding does not require that the// string ends with a NUL character. Thus, all n bytes are available for output.//// If operating in binary mode, this assumes strict GNU binary mode; which means// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is// equivalent to the sign bit in two's complement form.func fitsInBase256( int, int64) bool { := uint(-1) * 8return >= 9 || ( >= -1<< && < 1<<)}// parseNumeric parses the input as being encoded in either base-256 or octal.// This function may return negative numbers.// If parsing fails or an integer overflow occurs, err will be set.func ( *parser) ( []byte) int64 {// Check for base-256 (binary) format first. // If the first bit is set, then all following bits constitute a two's // complement encoded number in big-endian byte order.iflen() > 0 && [0]&0x80 != 0 {// Handling negative numbers relies on the following identity: // -a-1 == ^a // // If the number is negative, we use an inversion mask to invert the // data bytes and treat the value as an unsigned number.varbyte// 0x00 if positive or zero, 0xff if negativeif [0]&0x40 != 0 { = 0xff }varuint64for , := range { ^= // Inverts c only if inv is 0xff, otherwise does nothingif == 0 { &= 0x7f// Ignore signal bit in first byte }if ( >> 56) > 0 { .err = ErrHeader// Integer overflowreturn0 } = <<8 | uint64() }if ( >> 63) > 0 { .err = ErrHeader// Integer overflowreturn0 }if == 0xff {return ^int64() }returnint64() }// Normal case is base-8 (octal) format.return .parseOctal()}// formatNumeric encodes x into b using base-8 (octal) encoding if possible.// Otherwise it will attempt to use base-256 (binary) encoding.func ( *formatter) ( []byte, int64) {iffitsInOctal(len(), ) { .formatOctal(, )return }iffitsInBase256(len(), ) {for := len() - 1; >= 0; -- { [] = byte() >>= 8 } [0] |= 0x80// Highest bit indicates binary formatreturn } .formatOctal(, 0) // Last resort, just write zero .err = ErrFieldTooLong}func ( *parser) ( []byte) int64 {// Because unused fields are filled with NULs, we need // to skip leading NULs. Fields may also be padded with // spaces or NULs. // So we remove leading and trailing NULs and spaces to // be sure. = bytes.Trim(, " \x00")iflen() == 0 {return0 } , := strconv.ParseUint(.parseString(), 8, 64)if != nil { .err = ErrHeader }returnint64()}func ( *formatter) ( []byte, int64) {if !fitsInOctal(len(), ) { = 0// Last resort, just write zero .err = ErrFieldTooLong } := strconv.FormatInt(, 8)// Add leading zeros, but leave room for a NUL.if := len() - len() - 1; > 0 { = strings.Repeat("0", ) + } .formatString(, )}// fitsInOctal reports whether the integer x fits in a field n-bytes long// using octal encoding with the appropriate NUL terminator.func fitsInOctal( int, int64) bool { := uint(-1) * 3return >= 0 && ( >= 22 || < 1<<)}// parsePAXTime takes a string of the form %d.%d as described in the PAX// specification. Note that this implementation allows for negative timestamps,// which is allowed for by the PAX specification, but not always portable.func parsePAXTime( string) (time.Time, error) {const = 9// Split string into seconds and sub-seconds parts. , , := strings.Cut(, ".")// Parse the seconds. , := strconv.ParseInt(, 10, 64)if != nil {returntime.Time{}, ErrHeader }iflen() == 0 {returntime.Unix(, 0), nil// No sub-second values }// Parse the nanoseconds.ifstrings.Trim(, "0123456789") != "" {returntime.Time{}, ErrHeader }iflen() < { += strings.Repeat("0", -len()) // Right pad } else { = [:] // Right truncate } , := strconv.ParseInt(, 10, 64) // Must succeediflen() > 0 && [0] == '-' {returntime.Unix(, -1*), nil// Negative correction }returntime.Unix(, ), nil}// 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( time.Time) ( string) { , := .Unix(), .Nanosecond()if == 0 {returnstrconv.FormatInt(, 10) }// If seconds is negative, then perform correction. := ""if < 0 { = "-"// Remember sign = -( + 1) // Add a second to secs = -( - 1e9) // Take that second away from nsecs }returnstrings.TrimRight(fmt.Sprintf("%s%d.%09d", , , ), "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// return the remainder as r.func parsePAXRecord( string) (, , string, error) {// The size field ends at the first space. , , := strings.Cut(, " ")if ! {return"", "", , ErrHeader }// Parse the first token as a decimal integer. , := strconv.ParseInt(, 10, 0) // Intentionally parse as native intif != nil || < 5 || > int64(len()) {return"", "", , ErrHeader } -= int64(len() + 1) // convert from index in s to index in restif <= 0 {return"", "", , ErrHeader }// Extract everything between the space and the final newline. , , := [:-1], [-1:], [:]if != "\n" {return"", "", , ErrHeader }// The first equals separates the key from the value. , , = strings.Cut(, "=")if ! {return"", "", , ErrHeader }if !validPAXRecord(, ) {return"", "", , ErrHeader }return , , , nil}// formatPAXRecord formats a single PAX record, prefixing it with the// appropriate length.func formatPAXRecord(, string) (string, error) {if !validPAXRecord(, ) {return"", ErrHeader }const = 3// Extra padding for ' ', '=', and '\n' := len() + len() + += len(strconv.Itoa()) := strconv.Itoa() + " " + + "=" + + "\n"// Final adjustment if adding size field increased the record size.iflen() != { = len() = strconv.Itoa() + " " + + "=" + + "\n" }return , nil}// validPAXRecord reports whether the key-value pair is valid where each// record is formatted as://// "%d %s=%s\n" % (size, key, value)//// Keys and values should be UTF-8, but the number of bad writers out there// forces us to be a more liberal.// Thus, we only reject all keys with NUL, and only reject NULs in values// for the PAX version of the USTAR string fields.// The key must not contain an '=' character.func validPAXRecord(, string) bool {if == "" || strings.Contains(, "=") {returnfalse }switch {casepaxPath, paxLinkpath, paxUname, paxGname:return !hasNUL()default:return !hasNUL() }}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.