Adam Miller f12359b
Index: src/pkg/archive/tar/common.go
Adam Miller f12359b
===================================================================
Adam Miller f12359b
--- a/src/pkg/archive/tar/common.go
Adam Miller f12359b
+++ b/src/pkg/archive/tar/common.go
Adam Miller f12359b
@@ -13,6 +13,7 @@
Adam Miller f12359b
 package tar
Adam Miller f12359b
 
Adam Miller f12359b
 import (
Adam Miller f12359b
+	"bytes"
Adam Miller f12359b
 	"errors"
Adam Miller f12359b
 	"fmt"
Adam Miller f12359b
 	"os"
Adam Miller f12359b
@@ -174,6 +175,23 @@
Adam Miller f12359b
 	c_ISSOCK = 0140000 // Socket
Adam Miller f12359b
 )
Adam Miller f12359b
 
Adam Miller f12359b
+// Keywords for the PAX Extended Header
Adam Miller f12359b
+const (
Adam Miller f12359b
+	paxAtime    = "atime"
Adam Miller f12359b
+	paxCharset  = "charset"
Adam Miller f12359b
+	paxComment  = "comment"
Adam Miller f12359b
+	paxCtime    = "ctime" // please note that ctime is not a valid pax header.
Adam Miller f12359b
+	paxGid      = "gid"
Adam Miller f12359b
+	paxGname    = "gname"
Adam Miller f12359b
+	paxLinkpath = "linkpath"
Adam Miller f12359b
+	paxMtime    = "mtime"
Adam Miller f12359b
+	paxPath     = "path"
Adam Miller f12359b
+	paxSize     = "size"
Adam Miller f12359b
+	paxUid      = "uid"
Adam Miller f12359b
+	paxUname    = "uname"
Adam Miller f12359b
+	paxNone     = ""
Adam Miller f12359b
+)
Adam Miller f12359b
+
Adam Miller f12359b
 // FileInfoHeader creates a partially-populated Header from fi.
Adam Miller f12359b
 // If fi describes a symlink, FileInfoHeader records link as the link target.
Adam Miller f12359b
 // If fi describes a directory, a slash is appended to the name.
Adam Miller f12359b
@@ -257,3 +275,25 @@
Adam Miller f12359b
 	b, *sp = s[0:n], s[n:]
Adam Miller f12359b
 	return
Adam Miller f12359b
 }
Adam Miller f12359b
+
Adam Miller f12359b
+func isASCII(s string) bool {
Adam Miller f12359b
+	for _, c := range s {
Adam Miller f12359b
+		if c >= 0x80 {
Adam Miller f12359b
+			return false
Adam Miller f12359b
+		}
Adam Miller f12359b
+	}
Adam Miller f12359b
+	return true
Adam Miller f12359b
+}
Adam Miller f12359b
+
Adam Miller f12359b
+func toASCII(s string) string {
Adam Miller f12359b
+	if isASCII(s) {
Adam Miller f12359b
+		return s
Adam Miller f12359b
+	}
Adam Miller f12359b
+	var buf bytes.Buffer
Adam Miller f12359b
+	for _, c := range s {
Adam Miller f12359b
+		if c < 0x80 {
Adam Miller f12359b
+			buf.WriteByte(byte(c))
Adam Miller f12359b
+		}
Adam Miller f12359b
+	}
Adam Miller f12359b
+	return buf.String()
Adam Miller f12359b
+}
Adam Miller f12359b
Index: src/pkg/archive/tar/reader.go
Adam Miller f12359b
===================================================================
Adam Miller f12359b
--- a/src/pkg/archive/tar/reader.go
Adam Miller f12359b
+++ b/src/pkg/archive/tar/reader.go
Adam Miller f12359b
@@ -95,45 +95,45 @@
Adam Miller f12359b
 func mergePAX(hdr *Header, headers map[string]string) error {
Adam Miller f12359b
 	for k, v := range headers {
Adam Miller f12359b
 		switch k {
Adam Miller f12359b
-		case "path":
Adam Miller f12359b
+		case paxPath:
Adam Miller f12359b
 			hdr.Name = v
Adam Miller f12359b
-		case "linkpath":
Adam Miller f12359b
+		case paxLinkpath:
Adam Miller f12359b
 			hdr.Linkname = v
Adam Miller f12359b
-		case "gname":
Adam Miller f12359b
+		case paxGname:
Adam Miller f12359b
 			hdr.Gname = v
Adam Miller f12359b
-		case "uname":
Adam Miller f12359b
+		case paxUname:
Adam Miller f12359b
 			hdr.Uname = v
Adam Miller f12359b
-		case "uid":
Adam Miller f12359b
+		case paxUid:
Adam Miller f12359b
 			uid, err := strconv.ParseInt(v, 10, 0)
Adam Miller f12359b
 			if err != nil {
Adam Miller f12359b
 				return err
Adam Miller f12359b
 			}
Adam Miller f12359b
 			hdr.Uid = int(uid)
Adam Miller f12359b
-		case "gid":
Adam Miller f12359b
+		case paxGid:
Adam Miller f12359b
 			gid, err := strconv.ParseInt(v, 10, 0)
Adam Miller f12359b
 			if err != nil {
Adam Miller f12359b
 				return err
Adam Miller f12359b
 			}
Adam Miller f12359b
 			hdr.Gid = int(gid)
Adam Miller f12359b
-		case "atime":
Adam Miller f12359b
+		case paxAtime:
Adam Miller f12359b
 			t, err := parsePAXTime(v)
Adam Miller f12359b
 			if err != nil {
Adam Miller f12359b
 				return err
Adam Miller f12359b
 			}
Adam Miller f12359b
 			hdr.AccessTime = t
Adam Miller f12359b
-		case "mtime":
Adam Miller f12359b
+		case paxMtime:
Adam Miller f12359b
 			t, err := parsePAXTime(v)
Adam Miller f12359b
 			if err != nil {
Adam Miller f12359b
 				return err
Adam Miller f12359b
 			}
Adam Miller f12359b
 			hdr.ModTime = t
Adam Miller f12359b
-		case "ctime":
Adam Miller f12359b
+		case paxCtime:
Adam Miller f12359b
 			t, err := parsePAXTime(v)
Adam Miller f12359b
 			if err != nil {
Adam Miller f12359b
 				return err
Adam Miller f12359b
 			}
Adam Miller f12359b
 			hdr.ChangeTime = t
Adam Miller f12359b
-		case "size":
Adam Miller f12359b
+		case paxSize:
Adam Miller f12359b
 			size, err := strconv.ParseInt(v, 10, 0)
Adam Miller f12359b
 			if err != nil {
Adam Miller f12359b
 				return err
Adam Miller f12359b
Index: src/pkg/archive/tar/writer.go
Adam Miller f12359b
===================================================================
Adam Miller f12359b
--- a/src/pkg/archive/tar/writer.go
Adam Miller f12359b
+++ b/src/pkg/archive/tar/writer.go
Adam Miller f12359b
@@ -24,6 +24,7 @@
Adam Miller f12359b
 	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
Adam Miller f12359b
 	ErrWriteAfterClose = errors.New("archive/tar: write after close")
Adam Miller f12359b
 	errNameTooLong     = errors.New("archive/tar: name too long")
Adam Miller f12359b
+	errInvalidHeader   = errors.New("archive/tar: header field too long or contains invalid values")
Adam Miller f12359b
 )
Adam Miller f12359b
 
Adam Miller f12359b
 // A Writer provides sequential writing of a tar archive in POSIX.1 format.
Adam Miller f12359b
@@ -37,6 +38,7 @@
Adam Miller f12359b
 	pad        int64 // amount of padding to write after current file entry
Adam Miller f12359b
 	closed     bool
Adam Miller f12359b
 	usedBinary bool // whether the binary numeric field extension was used
Adam Miller f12359b
+	preferPax  bool // use pax header instead of binary numeric header
Adam Miller f12359b
 }
Adam Miller f12359b
 
Adam Miller f12359b
 // NewWriter creates a new Writer writing to w.
Adam Miller f12359b
@@ -65,16 +67,23 @@
Adam Miller f12359b
 }
Adam Miller f12359b
 
Adam Miller f12359b
 // Write s into b, terminating it with a NUL if there is room.
Adam Miller f12359b
-func (tw *Writer) cString(b []byte, s string) {
Adam Miller f12359b
+// If the value is too long for the field and allowPax is true add a paxheader record instead
Adam Miller f12359b
+func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
Adam Miller f12359b
+	needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
Adam Miller f12359b
+	if needsPaxHeader {
Adam Miller f12359b
+		paxHeaders[paxKeyword] = s
Adam Miller f12359b
+		return
Adam Miller f12359b
+	}
Adam Miller f12359b
 	if len(s) > len(b) {
Adam Miller f12359b
 		if tw.err == nil {
Adam Miller f12359b
 			tw.err = ErrFieldTooLong
Adam Miller f12359b
 		}
Adam Miller f12359b
 		return
Adam Miller f12359b
 	}
Adam Miller f12359b
-	copy(b, s)
Adam Miller f12359b
-	if len(s) < len(b) {
Adam Miller f12359b
-		b[len(s)] = 0
Adam Miller f12359b
+	ascii := toASCII(s)
Adam Miller f12359b
+	copy(b, ascii)
Adam Miller f12359b
+	if len(ascii) < len(b) {
Adam Miller f12359b
+		b[len(ascii)] = 0
Adam Miller f12359b
 	}
Adam Miller f12359b
 }
Adam Miller f12359b
 
Adam Miller f12359b
@@ -85,17 +94,27 @@
Adam Miller f12359b
 	for len(s)+1 < len(b) {
Adam Miller f12359b
 		s = "0" + s
Adam Miller f12359b
 	}
Adam Miller f12359b
-	tw.cString(b, s)
Adam Miller f12359b
+	tw.cString(b, s, false, paxNone, nil)
Adam Miller f12359b
 }
Adam Miller f12359b
 
Adam Miller f12359b
 // Write x into b, either as octal or as binary (GNUtar/star extension).
Adam Miller f12359b
-func (tw *Writer) numeric(b []byte, x int64) {
Adam Miller f12359b
+// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
Adam Miller f12359b
+func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
Adam Miller f12359b
 	// Try octal first.
Adam Miller f12359b
 	s := strconv.FormatInt(x, 8)
Adam Miller f12359b
 	if len(s) < len(b) {
Adam Miller f12359b
 		tw.octal(b, x)
Adam Miller f12359b
 		return
Adam Miller f12359b
 	}
Adam Miller f12359b
+
Adam Miller f12359b
+	// If it is too long for octal, and pax is preferred, use a pax header
Adam Miller f12359b
+	if allowPax && tw.preferPax {
Adam Miller f12359b
+		tw.octal(b, 0)
Adam Miller f12359b
+		s := strconv.FormatInt(x, 10)
Adam Miller f12359b
+		paxHeaders[paxKeyword] = s
Adam Miller f12359b
+		return
Adam Miller f12359b
+	}
Adam Miller f12359b
+
Adam Miller f12359b
 	// Too big: use binary (big-endian).
Adam Miller f12359b
 	tw.usedBinary = true
Adam Miller f12359b
 	for i := len(b) - 1; x > 0 && i >= 0; i-- {
Adam Miller f12359b
@@ -115,6 +134,15 @@
Adam Miller f12359b
 // WriteHeader calls Flush if it is not the first header.
Adam Miller f12359b
 // Calling after a Close will return ErrWriteAfterClose.
Adam Miller f12359b
 func (tw *Writer) WriteHeader(hdr *Header) error {
Adam Miller f12359b
+	return tw.writeHeader(hdr, true)
Adam Miller f12359b
+}
Adam Miller f12359b
+
Adam Miller f12359b
+// WriteHeader writes hdr and prepares to accept the file's contents.
Adam Miller f12359b
+// WriteHeader calls Flush if it is not the first header.
Adam Miller f12359b
+// Calling after a Close will return ErrWriteAfterClose.
Adam Miller f12359b
+// As this method is called internally by writePax header to allow it to
Adam Miller f12359b
+// suppress writing the pax header.
Adam Miller f12359b
+func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
Adam Miller f12359b
 	if tw.closed {
Adam Miller f12359b
 		return ErrWriteAfterClose
Adam Miller f12359b
 	}
Adam Miller f12359b
@@ -124,31 +152,21 @@
Adam Miller f12359b
 	if tw.err != nil {
Adam Miller f12359b
 		return tw.err
Adam Miller f12359b
 	}
Adam Miller f12359b
-	// Decide whether or not to use PAX extensions
Adam Miller f12359b
+
Adam Miller f12359b
+	// a map to hold pax header records, if any are needed
Adam Miller f12359b
+	paxHeaders := make(map[string]string)
Adam Miller f12359b
+
Adam Miller f12359b
 	// TODO(shanemhansen): we might want to use PAX headers for
Adam Miller f12359b
 	// subsecond time resolution, but for now let's just capture
Adam Miller f12359b
-	// the long name/long symlink use case.
Adam Miller f12359b
-	suffix := hdr.Name
Adam Miller f12359b
-	prefix := ""
Adam Miller f12359b
-	if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize {
Adam Miller f12359b
-		var err error
Adam Miller f12359b
-		prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
Adam Miller f12359b
-		// Either we were unable to pack the long name into ustar format
Adam Miller f12359b
-		// or the link name is too long; use PAX headers.
Adam Miller f12359b
-		if err == errNameTooLong || len(hdr.Linkname) > fileNameSize {
Adam Miller f12359b
-			if err := tw.writePAXHeader(hdr); err != nil {
Adam Miller f12359b
-				return err
Adam Miller f12359b
-			}
Adam Miller f12359b
-		} else if err != nil {
Adam Miller f12359b
-			return err
Adam Miller f12359b
-		}
Adam Miller f12359b
-	}
Adam Miller f12359b
-	tw.nb = int64(hdr.Size)
Adam Miller f12359b
-	tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
Adam Miller f12359b
+	// too long fields or non ascii characters
Adam Miller f12359b
 
Adam Miller f12359b
 	header := make([]byte, blockSize)
Adam Miller f12359b
 	s := slicer(header)
Adam Miller f12359b
-	tw.cString(s.next(fileNameSize), suffix)
Adam Miller f12359b
+
Adam Miller f12359b
+	// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
Adam Miller f12359b
+	pathHeaderBytes := s.next(fileNameSize)
Adam Miller f12359b
+
Adam Miller f12359b
+	tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
Adam Miller f12359b
 
Adam Miller f12359b
 	// Handle out of range ModTime carefully.
Adam Miller f12359b
 	var modTime int64
Adam Miller f12359b
@@ -156,27 +174,55 @@
Adam Miller f12359b
 		modTime = hdr.ModTime.Unix()
Adam Miller f12359b
 	}
Adam Miller f12359b
 
Adam Miller f12359b
-	tw.octal(s.next(8), hdr.Mode)          // 100:108
Adam Miller f12359b
-	tw.numeric(s.next(8), int64(hdr.Uid))  // 108:116
Adam Miller f12359b
-	tw.numeric(s.next(8), int64(hdr.Gid))  // 116:124
Adam Miller f12359b
-	tw.numeric(s.next(12), hdr.Size)       // 124:136
Adam Miller f12359b
-	tw.numeric(s.next(12), modTime)        // 136:148
Adam Miller f12359b
-	s.next(8)                              // chksum (148:156)
Adam Miller f12359b
-	s.next(1)[0] = hdr.Typeflag            // 156:157
Adam Miller f12359b
-	tw.cString(s.next(100), hdr.Linkname)  // linkname (157:257)
Adam Miller f12359b
-	copy(s.next(8), []byte("ustar\x0000")) // 257:265
Adam Miller f12359b
-	tw.cString(s.next(32), hdr.Uname)      // 265:297
Adam Miller f12359b
-	tw.cString(s.next(32), hdr.Gname)      // 297:329
Adam Miller f12359b
-	tw.numeric(s.next(8), hdr.Devmajor)    // 329:337
Adam Miller f12359b
-	tw.numeric(s.next(8), hdr.Devminor)    // 337:345
Adam Miller f12359b
-	tw.cString(s.next(155), prefix)        // 345:500
Adam Miller f12359b
+	tw.octal(s.next(8), hdr.Mode)                                   // 100:108
Adam Miller f12359b
+	tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
Adam Miller f12359b
+	tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
Adam Miller f12359b
+	tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders)     // 124:136
Adam Miller f12359b
+	tw.numeric(s.next(12), modTime, false, paxNone, nil)            // 136:148 --- consider using pax for finer granularity
Adam Miller f12359b
+	s.next(8)                                                       // chksum (148:156)
Adam Miller f12359b
+	s.next(1)[0] = hdr.Typeflag                                     // 156:157
Adam Miller f12359b
+
Adam Miller f12359b
+	tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
Adam Miller f12359b
+
Adam Miller f12359b
+	copy(s.next(8), []byte("ustar\x0000"))                        // 257:265
Adam Miller f12359b
+	tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
Adam Miller f12359b
+	tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
Adam Miller f12359b
+	tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil)      // 329:337
Adam Miller f12359b
+	tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil)      // 337:345
Adam Miller f12359b
+
Adam Miller f12359b
+	// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
Adam Miller f12359b
+	prefixHeaderBytes := s.next(155)
Adam Miller f12359b
+	tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500  prefix
Adam Miller f12359b
+
Adam Miller f12359b
 	// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
Adam Miller f12359b
 	if tw.usedBinary {
Adam Miller f12359b
 		copy(header[257:265], []byte("ustar  \x00"))
Adam Miller f12359b
 	}
Adam Miller f12359b
-	// Use the ustar magic if we used ustar long names.
Adam Miller f12359b
-	if len(prefix) > 0 {
Adam Miller f12359b
-		copy(header[257:265], []byte("ustar\000"))
Adam Miller f12359b
+
Adam Miller f12359b
+	_, paxPathUsed := paxHeaders[paxPath]
Adam Miller f12359b
+	// try to use a ustar header when only the name is too long
Adam Miller f12359b
+	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
Adam Miller f12359b
+		suffix := hdr.Name
Adam Miller f12359b
+		prefix := ""
Adam Miller f12359b
+		if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
Adam Miller f12359b
+			var err error
Adam Miller f12359b
+			prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
Adam Miller f12359b
+			if err == nil {
Adam Miller f12359b
+				// ok we can use a ustar long name instead of pax, now correct the fields
Adam Miller f12359b
+
Adam Miller f12359b
+				// remove the path field from the pax header. this will suppress the pax header
Adam Miller f12359b
+				delete(paxHeaders, paxPath)
Adam Miller f12359b
+
Adam Miller f12359b
+				// update the path fields
Adam Miller f12359b
+				tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
Adam Miller f12359b
+				tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
Adam Miller f12359b
+
Adam Miller f12359b
+				// Use the ustar magic if we used ustar long names.
Adam Miller f12359b
+				if len(prefix) > 0 {
Adam Miller f12359b
+					copy(header[257:265], []byte("ustar\000"))
Adam Miller f12359b
+				}
Adam Miller f12359b
+			}
Adam Miller f12359b
+		}
Adam Miller f12359b
 	}
Adam Miller f12359b
 
Adam Miller f12359b
 	// The chksum field is terminated by a NUL and a space.
Adam Miller f12359b
@@ -190,8 +236,18 @@
Adam Miller f12359b
 		return tw.err
Adam Miller f12359b
 	}
Adam Miller f12359b
 
Adam Miller f12359b
+	if len(paxHeaders) > 0 {
Adam Miller f12359b
+		if !allowPax {
Adam Miller f12359b
+			return errInvalidHeader
Adam Miller f12359b
+		}
Adam Miller f12359b
+		if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
Adam Miller f12359b
+			return err
Adam Miller f12359b
+		}
Adam Miller f12359b
+	}
Adam Miller f12359b
+	tw.nb = int64(hdr.Size)
Adam Miller f12359b
+	tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
Adam Miller f12359b
+
Adam Miller f12359b
 	_, tw.err = tw.w.Write(header)
Adam Miller f12359b
-
Adam Miller f12359b
 	return tw.err
Adam Miller f12359b
 }
Adam Miller f12359b
 
Adam Miller f12359b
@@ -218,7 +274,7 @@
Adam Miller f12359b
 
Adam Miller f12359b
 // writePaxHeader writes an extended pax header to the
Adam Miller f12359b
 // archive.
Adam Miller f12359b
-func (tw *Writer) writePAXHeader(hdr *Header) error {
Adam Miller f12359b
+func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
Adam Miller f12359b
 	// Prepare extended header
Adam Miller f12359b
 	ext := new(Header)
Adam Miller f12359b
 	ext.Typeflag = TypeXHeader
Adam Miller f12359b
@@ -229,18 +285,23 @@
Adam Miller f12359b
 	// with the current pid.
Adam Miller f12359b
 	pid := os.Getpid()
Adam Miller f12359b
 	dir, file := path.Split(hdr.Name)
Adam Miller f12359b
-	ext.Name = path.Join(dir,
Adam Miller f12359b
-		fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100]
Adam Miller f12359b
+	fullName := path.Join(dir,
Adam Miller f12359b
+		fmt.Sprintf("PaxHeaders.%d", pid), file)
Adam Miller f12359b
+
Adam Miller f12359b
+	ascii := toASCII(fullName)
Adam Miller f12359b
+	if len(ascii) > 100 {
Adam Miller f12359b
+		ascii = ascii[:100]
Adam Miller f12359b
+	}
Adam Miller f12359b
+	ext.Name = ascii
Adam Miller f12359b
 	// Construct the body
Adam Miller f12359b
 	var buf bytes.Buffer
Adam Miller f12359b
-	if len(hdr.Name) > fileNameSize {
Adam Miller f12359b
-		fmt.Fprint(&buf, paxHeader("path="+hdr.Name))
Adam Miller f12359b
+
Adam Miller f12359b
+	for k, v := range paxHeaders {
Adam Miller f12359b
+		fmt.Fprint(&buf, paxHeader(k+"="+v))
Adam Miller f12359b
 	}
Adam Miller f12359b
-	if len(hdr.Linkname) > fileNameSize {
Adam Miller f12359b
-		fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname))
Adam Miller f12359b
-	}
Adam Miller f12359b
+
Adam Miller f12359b
 	ext.Size = int64(len(buf.Bytes()))
Adam Miller f12359b
-	if err := tw.WriteHeader(ext); err != nil {
Adam Miller f12359b
+	if err := tw.writeHeader(ext, false); err != nil {
Adam Miller f12359b
 		return err
Adam Miller f12359b
 	}
Adam Miller f12359b
 	if _, err := tw.Write(buf.Bytes()); err != nil {
Adam Miller f12359b
Index: src/pkg/archive/tar/writer_test.go
Adam Miller f12359b
===================================================================
Adam Miller f12359b
--- a/src/pkg/archive/tar/writer_test.go
Adam Miller f12359b
+++ b/src/pkg/archive/tar/writer_test.go
Adam Miller f12359b
@@ -243,15 +243,110 @@
Adam Miller f12359b
 	}
Adam Miller f12359b
 }
Adam Miller f12359b
 
Adam Miller f12359b
+func TestPaxSymlink(t *testing.T) {
Adam Miller f12359b
+	// Create an archive with a large linkname
Adam Miller f12359b
+	fileinfo, err := os.Stat("testdata/small.txt")
Adam Miller f12359b
+	if err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	hdr, err := FileInfoHeader(fileinfo, "")
Adam Miller f12359b
+	hdr.Typeflag = TypeSymlink
Adam Miller f12359b
+	if err != nil {
Adam Miller f12359b
+		t.Fatalf("os.Stat:1 %v", err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	// Force a PAX long linkname to be written
Adam Miller f12359b
+	longLinkname := strings.Repeat("1234567890/1234567890", 10)
Adam Miller f12359b
+	hdr.Linkname = longLinkname
Adam Miller f12359b
+
Adam Miller f12359b
+	hdr.Size = 0
Adam Miller f12359b
+	var buf bytes.Buffer
Adam Miller f12359b
+	writer := NewWriter(&buf)
Adam Miller f12359b
+	if err := writer.WriteHeader(hdr); err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if err := writer.Close(); err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	// Simple test to make sure PAX extensions are in effect
Adam Miller f12359b
+	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
Adam Miller f12359b
+		t.Fatal("Expected at least one PAX header to be written.")
Adam Miller f12359b
+	}
Adam Miller f12359b
+	// Test that we can get a long name back out of the archive.
Adam Miller f12359b
+	reader := NewReader(&buf)
Adam Miller f12359b
+	hdr, err = reader.Next()
Adam Miller f12359b
+	if err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if hdr.Linkname != longLinkname {
Adam Miller f12359b
+		t.Fatal("Couldn't recover long link name")
Adam Miller f12359b
+	}
Adam Miller f12359b
+}
Adam Miller f12359b
+
Adam Miller f12359b
+func TestPaxNonAscii(t *testing.T) {
Adam Miller f12359b
+	// Create an archive with non ascii. These should trigger a pax header
Adam Miller f12359b
+	// because pax headers have a defined utf-8 encoding.
Adam Miller f12359b
+	fileinfo, err := os.Stat("testdata/small.txt")
Adam Miller f12359b
+	if err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+
Adam Miller f12359b
+	hdr, err := FileInfoHeader(fileinfo, "")
Adam Miller f12359b
+	if err != nil {
Adam Miller f12359b
+		t.Fatalf("os.Stat:1 %v", err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+
Adam Miller f12359b
+	// some sample data
Adam Miller f12359b
+	chineseFilename := "文件名"
Adam Miller f12359b
+	chineseGroupname := "組"
Adam Miller f12359b
+	chineseUsername := "用戶名"
Adam Miller f12359b
+
Adam Miller f12359b
+	hdr.Name = chineseFilename
Adam Miller f12359b
+	hdr.Gname = chineseGroupname
Adam Miller f12359b
+	hdr.Uname = chineseUsername
Adam Miller f12359b
+
Adam Miller f12359b
+	contents := strings.Repeat(" ", int(hdr.Size))
Adam Miller f12359b
+
Adam Miller f12359b
+	var buf bytes.Buffer
Adam Miller f12359b
+	writer := NewWriter(&buf)
Adam Miller f12359b
+	if err := writer.WriteHeader(hdr); err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if _, err = writer.Write([]byte(contents)); err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if err := writer.Close(); err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	// Simple test to make sure PAX extensions are in effect
Adam Miller f12359b
+	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
Adam Miller f12359b
+		t.Fatal("Expected at least one PAX header to be written.")
Adam Miller f12359b
+	}
Adam Miller f12359b
+	// Test that we can get a long name back out of the archive.
Adam Miller f12359b
+	reader := NewReader(&buf)
Adam Miller f12359b
+	hdr, err = reader.Next()
Adam Miller f12359b
+	if err != nil {
Adam Miller f12359b
+		t.Fatal(err)
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if hdr.Name != chineseFilename {
Adam Miller f12359b
+		t.Fatal("Couldn't recover unicode name")
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if hdr.Gname != chineseGroupname {
Adam Miller f12359b
+		t.Fatal("Couldn't recover unicode group")
Adam Miller f12359b
+	}
Adam Miller f12359b
+	if hdr.Uname != chineseUsername {
Adam Miller f12359b
+		t.Fatal("Couldn't recover unicode user")
Adam Miller f12359b
+	}
Adam Miller f12359b
+}
Adam Miller f12359b
+
Adam Miller f12359b
 func TestPAXHeader(t *testing.T) {
Adam Miller f12359b
 	medName := strings.Repeat("CD", 50)
Adam Miller f12359b
 	longName := strings.Repeat("AB", 100)
Adam Miller f12359b
 	paxTests := [][2]string{
Adam Miller f12359b
-		{"name=/etc/hosts", "19 name=/etc/hosts\n"},
Adam Miller f12359b
+		{paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
Adam Miller f12359b
 		{"a=b", "6 a=b\n"},          // Single digit length
Adam Miller f12359b
 		{"a=names", "11 a=names\n"}, // Test case involving carries
Adam Miller f12359b
-		{"name=" + longName, fmt.Sprintf("210 name=%s\n", longName)},
Adam Miller f12359b
-		{"name=" + medName, fmt.Sprintf("110 name=%s\n", medName)}}
Adam Miller f12359b
+		{paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
Adam Miller f12359b
+		{paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
Adam Miller f12359b
 
Adam Miller f12359b
 	for _, test := range paxTests {
Adam Miller f12359b
 		key, expected := test[0], test[1]