src/pkg/archive/tar/writer.go - The Go Programming Language

Golang

Source file src/pkg/archive/tar/writer.go

     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		"errors"
    12		"fmt"
    13		"io"
    14		"strconv"
    15	)
    16	
    17	var (
    18		ErrWriteTooLong    = errors.New("archive/tar: write too long")
    19		ErrFieldTooLong    = errors.New("archive/tar: header field too long")
    20		ErrWriteAfterClose = errors.New("archive/tar: write after close")
    21	)
    22	
    23	// A Writer provides sequential writing of a tar archive in POSIX.1 format.
    24	// A tar archive consists of a sequence of files.
    25	// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
    26	// writing at most hdr.Size bytes in total.
    27	//
    28	// Example:
    29	//	tw := tar.NewWriter(w)
    30	//	hdr := new(Header)
    31	//	hdr.Size = length of data in bytes
    32	//	// populate other hdr fields as desired
    33	//	if err := tw.WriteHeader(hdr); err != nil {
    34	//		// handle error
    35	//	}
    36	//	io.Copy(tw, data)
    37	//	tw.Close()
    38	type Writer struct {
    39		w          io.Writer
    40		err        error
    41		nb         int64 // number of unwritten bytes for current file entry
    42		pad        int64 // amount of padding to write after current file entry
    43		closed     bool
    44		usedBinary bool // whether the binary numeric field extension was used
    45	}
    46	
    47	// NewWriter creates a new Writer writing to w.
    48	func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
    49	
    50	// Flush finishes writing the current file (optional).
    51	func (tw *Writer) Flush() error {
    52		if tw.nb > 0 {
    53			tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb)
    54			return tw.err
    55		}
    56	
    57		n := tw.nb + tw.pad
    58		for n > 0 && tw.err == nil {
    59			nr := n
    60			if nr > blockSize {
    61				nr = blockSize
    62			}
    63			var nw int
    64			nw, tw.err = tw.w.Write(zeroBlock[0:nr])
    65			n -= int64(nw)
    66		}
    67		tw.nb = 0
    68		tw.pad = 0
    69		return tw.err
    70	}
    71	
    72	// Write s into b, terminating it with a NUL if there is room.
    73	func (tw *Writer) cString(b []byte, s string) {
    74		if len(s) > len(b) {
    75			if tw.err == nil {
    76				tw.err = ErrFieldTooLong
    77			}
    78			return
    79		}
    80		copy(b, s)
    81		if len(s) < len(b) {
    82			b[len(s)] = 0
    83		}
    84	}
    85	
    86	// Encode x as an octal ASCII string and write it into b with leading zeros.
    87	func (tw *Writer) octal(b []byte, x int64) {
    88		s := strconv.FormatInt(x, 8)
    89		// leading zeros, but leave room for a NUL.
    90		for len(s)+1 < len(b) {
    91			s = "0" + s
    92		}
    93		tw.cString(b, s)
    94	}
    95	
    96	// Write x into b, either as octal or as binary (GNUtar/star extension).
    97	func (tw *Writer) numeric(b []byte, x int64) {
    98		// Try octal first.
    99		s := strconv.FormatInt(x, 8)
   100		if len(s) < len(b) {
   101			tw.octal(b, x)
   102			return
   103		}
   104		// Too big: use binary (big-endian).
   105		tw.usedBinary = true
   106		for i := len(b) - 1; x > 0 && i >= 0; i-- {
   107			b[i] = byte(x)
   108			x >>= 8
   109		}
   110		b[0] |= 0x80 // highest bit indicates binary format
   111	}
   112	
   113	// WriteHeader writes hdr and prepares to accept the file's contents.
   114	// WriteHeader calls Flush if it is not the first header.
   115	// Calling after a Close will return ErrWriteAfterClose.
   116	func (tw *Writer) WriteHeader(hdr *Header) error {
   117		if tw.closed {
   118			return ErrWriteAfterClose
   119		}
   120		if tw.err == nil {
   121			tw.Flush()
   122		}
   123		if tw.err != nil {
   124			return tw.err
   125		}
   126	
   127		tw.nb = int64(hdr.Size)
   128		tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
   129	
   130		header := make([]byte, blockSize)
   131		s := slicer(header)
   132	
   133		// TODO(dsymonds): handle names longer than 100 chars
   134		copy(s.next(100), []byte(hdr.Name))
   135	
   136		tw.octal(s.next(8), hdr.Mode)              // 100:108
   137		tw.numeric(s.next(8), int64(hdr.Uid))      // 108:116
   138		tw.numeric(s.next(8), int64(hdr.Gid))      // 116:124
   139		tw.numeric(s.next(12), hdr.Size)           // 124:136
   140		tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148
   141		s.next(8)                                  // chksum (148:156)
   142		s.next(1)[0] = hdr.Typeflag                // 156:157
   143		tw.cString(s.next(100), hdr.Linkname)      // linkname (157:257)
   144		copy(s.next(8), []byte("ustar\x0000"))     // 257:265
   145		tw.cString(s.next(32), hdr.Uname)          // 265:297
   146		tw.cString(s.next(32), hdr.Gname)          // 297:329
   147		tw.numeric(s.next(8), hdr.Devmajor)        // 329:337
   148		tw.numeric(s.next(8), hdr.Devminor)        // 337:345
   149	
   150		// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
   151		if tw.usedBinary {
   152			copy(header[257:265], []byte("ustar  \x00"))
   153		}
   154	
   155		// The chksum field is terminated by a NUL and a space.
   156		// This is different from the other octal fields.
   157		chksum, _ := checksum(header)
   158		tw.octal(header[148:155], chksum)
   159		header[155] = ' '
   160	
   161		if tw.err != nil {
   162			// problem with header; probably integer too big for a field.
   163			return tw.err
   164		}
   165	
   166		_, tw.err = tw.w.Write(header)
   167	
   168		return tw.err
   169	}
   170	
   171	// Write writes to the current entry in the tar archive.
   172	// Write returns the error ErrWriteTooLong if more than
   173	// hdr.Size bytes are written after WriteHeader.
   174	func (tw *Writer) Write(b []byte) (n int, err error) {
   175		if tw.closed {
   176			err = ErrWriteTooLong
   177			return
   178		}
   179		overwrite := false
   180		if int64(len(b)) > tw.nb {
   181			b = b[0:tw.nb]
   182			overwrite = true
   183		}
   184		n, err = tw.w.Write(b)
   185		tw.nb -= int64(n)
   186		if err == nil && overwrite {
   187			err = ErrWriteTooLong
   188			return
   189		}
   190		tw.err = err
   191		return
   192	}
   193	
   194	// Close closes the tar archive, flushing any unwritten
   195	// data to the underlying writer.
   196	func (tw *Writer) Close() error {
   197		if tw.err != nil || tw.closed {
   198			return tw.err
   199		}
   200		tw.Flush()
   201		tw.closed = true
   202		if tw.err != nil {
   203			return tw.err
   204		}
   205	
   206		// trailer: two zero blocks
   207		for i := 0; i < 2; i++ {
   208			_, tw.err = tw.w.Write(zeroBlock)
   209			if tw.err != nil {
   210				break
   211			}
   212		}
   213		return tw.err
   214	}