github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/archive/tar/writer.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tar
     6  
     7  // TODO(dsymonds):
     8  // - catch more errors (no first header, etc.)
     9  
    10  import (
    11  	"bytes"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"path"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  )
    21  
    22  var (
    23  	ErrWriteTooLong    = errors.New("archive/tar: write too long")
    24  	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
    25  	ErrWriteAfterClose = errors.New("archive/tar: write after close")
    26  	errNameTooLong     = errors.New("archive/tar: name too long")
    27  	errInvalidHeader   = errors.New("archive/tar: header field too long or contains invalid values")
    28  )
    29  
    30  // A Writer provides sequential writing of a tar archive in POSIX.1 format.
    31  // A tar archive consists of a sequence of files.
    32  // Call WriteHeader to begin a new file, and then call Write to supply that file's data,
    33  // writing at most hdr.Size bytes in total.
    34  type Writer struct {
    35  	w          io.Writer
    36  	err        error
    37  	nb         int64 // number of unwritten bytes for current file entry
    38  	pad        int64 // amount of padding to write after current file entry
    39  	closed     bool
    40  	usedBinary bool // whether the binary numeric field extension was used
    41  	preferPax  bool // use pax header instead of binary numeric header
    42  }
    43  
    44  // NewWriter creates a new Writer writing to w.
    45  func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
    46  
    47  // Flush finishes writing the current file (optional).
    48  func (tw *Writer) Flush() error {
    49  	if tw.nb > 0 {
    50  		tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
    51  		return tw.err
    52  	}
    53  
    54  	n := tw.nb + tw.pad
    55  	for n > 0 && tw.err == nil {
    56  		nr := n
    57  		if nr > blockSize {
    58  			nr = blockSize
    59  		}
    60  		var nw int
    61  		nw, tw.err = tw.w.Write(zeroBlock[0:nr])
    62  		n -= int64(nw)
    63  	}
    64  	tw.nb = 0
    65  	tw.pad = 0
    66  	return tw.err
    67  }
    68  
    69  // Write s into b, terminating it with a NUL if there is room.
    70  // If the value is too long for the field and allowPax is true add a paxheader record instead
    71  func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
    72  	needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
    73  	if needsPaxHeader {
    74  		paxHeaders[paxKeyword] = s
    75  		return
    76  	}
    77  	if len(s) > len(b) {
    78  		if tw.err == nil {
    79  			tw.err = ErrFieldTooLong
    80  		}
    81  		return
    82  	}
    83  	ascii := toASCII(s)
    84  	copy(b, ascii)
    85  	if len(ascii) < len(b) {
    86  		b[len(ascii)] = 0
    87  	}
    88  }
    89  
    90  // Encode x as an octal ASCII string and write it into b with leading zeros.
    91  func (tw *Writer) octal(b []byte, x int64) {
    92  	s := strconv.FormatInt(x, 8)
    93  	// leading zeros, but leave room for a NUL.
    94  	for len(s)+1 < len(b) {
    95  		s = "0" + s
    96  	}
    97  	tw.cString(b, s, false, paxNone, nil)
    98  }
    99  
   100  // Write x into b, either as octal or as binary (GNUtar/star extension).
   101  // If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
   102  func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
   103  	// Try octal first.
   104  	s := strconv.FormatInt(x, 8)
   105  	if len(s) < len(b) {
   106  		tw.octal(b, x)
   107  		return
   108  	}
   109  
   110  	// If it is too long for octal, and pax is preferred, use a pax header
   111  	if allowPax && tw.preferPax {
   112  		tw.octal(b, 0)
   113  		s := strconv.FormatInt(x, 10)
   114  		paxHeaders[paxKeyword] = s
   115  		return
   116  	}
   117  
   118  	// Too big: use binary (big-endian).
   119  	tw.usedBinary = true
   120  	for i := len(b) - 1; x > 0 && i >= 0; i-- {
   121  		b[i] = byte(x)
   122  		x >>= 8
   123  	}
   124  	b[0] |= 0x80 // highest bit indicates binary format
   125  }
   126  
   127  var (
   128  	minTime = time.Unix(0, 0)
   129  	// There is room for 11 octal digits (33 bits) of mtime.
   130  	maxTime = minTime.Add((1<<33 - 1) * time.Second)
   131  )
   132  
   133  // WriteHeader writes hdr and prepares to accept the file's contents.
   134  // WriteHeader calls Flush if it is not the first header.
   135  // Calling after a Close will return ErrWriteAfterClose.
   136  func (tw *Writer) WriteHeader(hdr *Header) error {
   137  	return tw.writeHeader(hdr, true)
   138  }
   139  
   140  // WriteHeader writes hdr and prepares to accept the file's contents.
   141  // WriteHeader calls Flush if it is not the first header.
   142  // Calling after a Close will return ErrWriteAfterClose.
   143  // As this method is called internally by writePax header to allow it to
   144  // suppress writing the pax header.
   145  func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
   146  	if tw.closed {
   147  		return ErrWriteAfterClose
   148  	}
   149  	if tw.err == nil {
   150  		tw.Flush()
   151  	}
   152  	if tw.err != nil {
   153  		return tw.err
   154  	}
   155  
   156  	// a map to hold pax header records, if any are needed
   157  	paxHeaders := make(map[string]string)
   158  
   159  	// TODO(shanemhansen): we might want to use PAX headers for
   160  	// subsecond time resolution, but for now let's just capture
   161  	// too long fields or non ascii characters
   162  
   163  	header := make([]byte, blockSize)
   164  	s := slicer(header)
   165  
   166  	// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
   167  	pathHeaderBytes := s.next(fileNameSize)
   168  
   169  	tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
   170  
   171  	// Handle out of range ModTime carefully.
   172  	var modTime int64
   173  	if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
   174  		modTime = hdr.ModTime.Unix()
   175  	}
   176  
   177  	tw.octal(s.next(8), hdr.Mode)                                   // 100:108
   178  	tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
   179  	tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
   180  	tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders)     // 124:136
   181  	tw.numeric(s.next(12), modTime, false, paxNone, nil)            // 136:148 --- consider using pax for finer granularity
   182  	s.next(8)                                                       // chksum (148:156)
   183  	s.next(1)[0] = hdr.Typeflag                                     // 156:157
   184  
   185  	tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
   186  
   187  	copy(s.next(8), []byte("ustar\x0000"))                        // 257:265
   188  	tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
   189  	tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
   190  	tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil)      // 329:337
   191  	tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil)      // 337:345
   192  
   193  	// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
   194  	prefixHeaderBytes := s.next(155)
   195  	tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500  prefix
   196  
   197  	// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
   198  	if tw.usedBinary {
   199  		copy(header[257:265], []byte("ustar  \x00"))
   200  	}
   201  
   202  	_, paxPathUsed := paxHeaders[paxPath]
   203  	// try to use a ustar header when only the name is too long
   204  	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
   205  		suffix := hdr.Name
   206  		prefix := ""
   207  		if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
   208  			var err error
   209  			prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
   210  			if err == nil {
   211  				// ok we can use a ustar long name instead of pax, now correct the fields
   212  
   213  				// remove the path field from the pax header. this will suppress the pax header
   214  				delete(paxHeaders, paxPath)
   215  
   216  				// update the path fields
   217  				tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
   218  				tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
   219  
   220  				// Use the ustar magic if we used ustar long names.
   221  				if len(prefix) > 0 {
   222  					copy(header[257:265], []byte("ustar\000"))
   223  				}
   224  			}
   225  		}
   226  	}
   227  
   228  	// The chksum field is terminated by a NUL and a space.
   229  	// This is different from the other octal fields.
   230  	chksum, _ := checksum(header)
   231  	tw.octal(header[148:155], chksum)
   232  	header[155] = ' '
   233  
   234  	if tw.err != nil {
   235  		// problem with header; probably integer too big for a field.
   236  		return tw.err
   237  	}
   238  
   239  	if len(paxHeaders) > 0 {
   240  		if !allowPax {
   241  			return errInvalidHeader
   242  		}
   243  		if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
   244  			return err
   245  		}
   246  	}
   247  	tw.nb = int64(hdr.Size)
   248  	tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
   249  
   250  	_, tw.err = tw.w.Write(header)
   251  	return tw.err
   252  }
   253  
   254  // writeUSTARLongName splits a USTAR long name hdr.Name.
   255  // name must be < 256 characters. errNameTooLong is returned
   256  // if hdr.Name can't be split. The splitting heuristic
   257  // is compatible with gnu tar.
   258  func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) {
   259  	length := len(name)
   260  	if length > fileNamePrefixSize+1 {
   261  		length = fileNamePrefixSize + 1
   262  	} else if name[length-1] == '/' {
   263  		length--
   264  	}
   265  	i := strings.LastIndex(name[:length], "/")
   266  	nlen := length - i - 1
   267  	if i <= 0 || nlen > fileNameSize || nlen == 0 {
   268  		err = errNameTooLong
   269  		return
   270  	}
   271  	prefix, suffix = name[:i], name[i+1:]
   272  	return
   273  }
   274  
   275  // writePaxHeader writes an extended pax header to the
   276  // archive.
   277  func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
   278  	// Prepare extended header
   279  	ext := new(Header)
   280  	ext.Typeflag = TypeXHeader
   281  	// Setting ModTime is required for reader parsing to
   282  	// succeed, and seems harmless enough.
   283  	ext.ModTime = hdr.ModTime
   284  	// The spec asks that we namespace our pseudo files
   285  	// with the current pid.
   286  	pid := os.Getpid()
   287  	dir, file := path.Split(hdr.Name)
   288  	fullName := path.Join(dir,
   289  		fmt.Sprintf("PaxHeaders.%d", pid), file)
   290  
   291  	ascii := toASCII(fullName)
   292  	if len(ascii) > 100 {
   293  		ascii = ascii[:100]
   294  	}
   295  	ext.Name = ascii
   296  	// Construct the body
   297  	var buf bytes.Buffer
   298  
   299  	for k, v := range paxHeaders {
   300  		fmt.Fprint(&buf, paxHeader(k+"="+v))
   301  	}
   302  
   303  	ext.Size = int64(len(buf.Bytes()))
   304  	if err := tw.writeHeader(ext, false); err != nil {
   305  		return err
   306  	}
   307  	if _, err := tw.Write(buf.Bytes()); err != nil {
   308  		return err
   309  	}
   310  	if err := tw.Flush(); err != nil {
   311  		return err
   312  	}
   313  	return nil
   314  }
   315  
   316  // paxHeader formats a single pax record, prefixing it with the appropriate length
   317  func paxHeader(msg string) string {
   318  	const padding = 2 // Extra padding for space and newline
   319  	size := len(msg) + padding
   320  	size += len(strconv.Itoa(size))
   321  	record := fmt.Sprintf("%d %s\n", size, msg)
   322  	if len(record) != size {
   323  		// Final adjustment if adding size increased
   324  		// the number of digits in size
   325  		size = len(record)
   326  		record = fmt.Sprintf("%d %s\n", size, msg)
   327  	}
   328  	return record
   329  }
   330  
   331  // Write writes to the current entry in the tar archive.
   332  // Write returns the error ErrWriteTooLong if more than
   333  // hdr.Size bytes are written after WriteHeader.
   334  func (tw *Writer) Write(b []byte) (n int, err error) {
   335  	if tw.closed {
   336  		err = ErrWriteTooLong
   337  		return
   338  	}
   339  	overwrite := false
   340  	if int64(len(b)) > tw.nb {
   341  		b = b[0:tw.nb]
   342  		overwrite = true
   343  	}
   344  	n, err = tw.w.Write(b)
   345  	tw.nb -= int64(n)
   346  	if err == nil && overwrite {
   347  		err = ErrWriteTooLong
   348  		return
   349  	}
   350  	tw.err = err
   351  	return
   352  }
   353  
   354  // Close closes the tar archive, flushing any unwritten
   355  // data to the underlying writer.
   356  func (tw *Writer) Close() error {
   357  	if tw.err != nil || tw.closed {
   358  		return tw.err
   359  	}
   360  	tw.Flush()
   361  	tw.closed = true
   362  	if tw.err != nil {
   363  		return tw.err
   364  	}
   365  
   366  	// trailer: two zero blocks
   367  	for i := 0; i < 2; i++ {
   368  		_, tw.err = tw.w.Write(zeroBlock)
   369  		if tw.err != nil {
   370  			break
   371  		}
   372  	}
   373  	return tw.err
   374  }