github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/src/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  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"path"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  // Writer provides sequential writing of a tar archive.
    19  // Write.WriteHeader begins a new file with the provided Header,
    20  // and then Writer can be treated as an io.Writer to supply that file's data.
    21  type Writer struct {
    22  	w    io.Writer
    23  	pad  int64      // Amount of padding to write after current file entry
    24  	curr fileWriter // Writer for current file entry
    25  	hdr  Header     // Shallow copy of Header that is safe for mutations
    26  	blk  block      // Buffer to use as temporary local storage
    27  
    28  	// err is a persistent error.
    29  	// It is only the responsibility of every exported method of Writer to
    30  	// ensure that this error is sticky.
    31  	err error
    32  }
    33  
    34  // NewWriter creates a new Writer writing to w.
    35  func NewWriter(w io.Writer) *Writer {
    36  	return &Writer{w: w, curr: &regFileWriter{w, 0}}
    37  }
    38  
    39  type fileWriter interface {
    40  	io.Writer
    41  	fileState
    42  
    43  	FillZeros(n int64) (int64, error)
    44  }
    45  
    46  // Flush finishes writing the current file's block padding.
    47  // The current file must be fully written before Flush can be called.
    48  //
    49  // Deprecated: This is unnecessary as the next call to WriteHeader or Close
    50  // will implicitly flush out the file's padding.
    51  func (tw *Writer) Flush() error {
    52  	if tw.err != nil {
    53  		return tw.err
    54  	}
    55  	if nb := tw.curr.Remaining(); nb > 0 {
    56  		return fmt.Errorf("tar: missed writing %d bytes", nb)
    57  	}
    58  	if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil {
    59  		return tw.err
    60  	}
    61  	tw.pad = 0
    62  	return nil
    63  }
    64  
    65  // WriteHeader writes hdr and prepares to accept the file's contents.
    66  // The Header.Size determines how many bytes can be written for the next file.
    67  // If the current file is not fully written, then this returns an error.
    68  // This implicitly flushes any padding necessary before writing the header.
    69  func (tw *Writer) WriteHeader(hdr *Header) error {
    70  	if err := tw.Flush(); err != nil {
    71  		return err
    72  	}
    73  
    74  	tw.hdr = *hdr // Shallow copy of Header
    75  	allowedFormats, paxHdrs, err := tw.hdr.allowedFormats()
    76  	switch {
    77  	case allowedFormats.has(FormatUSTAR):
    78  		tw.err = tw.writeUSTARHeader(&tw.hdr)
    79  		return tw.err
    80  	case allowedFormats.has(FormatPAX):
    81  		tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs)
    82  		return tw.err
    83  	case allowedFormats.has(FormatGNU):
    84  		tw.err = tw.writeGNUHeader(&tw.hdr)
    85  		return tw.err
    86  	default:
    87  		return err // Non-fatal error
    88  	}
    89  }
    90  
    91  func (tw *Writer) writeUSTARHeader(hdr *Header) error {
    92  	// Check if we can use USTAR prefix/suffix splitting.
    93  	var namePrefix string
    94  	if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok {
    95  		namePrefix, hdr.Name = prefix, suffix
    96  	}
    97  
    98  	// Pack the main header.
    99  	var f formatter
   100  	blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
   101  	f.formatString(blk.USTAR().Prefix(), namePrefix)
   102  	blk.SetFormat(FormatUSTAR)
   103  	if f.err != nil {
   104  		return f.err // Should never happen since header is validated
   105  	}
   106  	return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag)
   107  }
   108  
   109  func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
   110  	realName, realSize := hdr.Name, hdr.Size
   111  
   112  	// Handle sparse files.
   113  	var spd sparseDatas
   114  	var spb []byte
   115  	if len(hdr.SparseHoles) > 0 {
   116  		sph := append([]SparseEntry{}, hdr.SparseHoles...) // Copy sparse map
   117  		sph = alignSparseEntries(sph, hdr.Size)
   118  		spd = invertSparseEntries(sph, hdr.Size)
   119  
   120  		// Format the sparse map.
   121  		hdr.Size = 0 // Replace with encoded size
   122  		spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n')
   123  		for _, s := range spd {
   124  			hdr.Size += s.Length
   125  			spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n')
   126  			spb = append(strconv.AppendInt(spb, s.Length, 10), '\n')
   127  		}
   128  		pad := blockPadding(int64(len(spb)))
   129  		spb = append(spb, zeroBlock[:pad]...)
   130  		hdr.Size += int64(len(spb)) // Accounts for encoded sparse map
   131  
   132  		// Add and modify appropriate PAX records.
   133  		dir, file := path.Split(realName)
   134  		hdr.Name = path.Join(dir, "GNUSparseFile.0", file)
   135  		paxHdrs[paxGNUSparseMajor] = "1"
   136  		paxHdrs[paxGNUSparseMinor] = "0"
   137  		paxHdrs[paxGNUSparseName] = realName
   138  		paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10)
   139  		paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10)
   140  		delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName
   141  	}
   142  
   143  	// Write PAX records to the output.
   144  	isGlobal := hdr.Typeflag == TypeXGlobalHeader
   145  	if len(paxHdrs) > 0 || isGlobal {
   146  		// Sort keys for deterministic ordering.
   147  		var keys []string
   148  		for k := range paxHdrs {
   149  			keys = append(keys, k)
   150  		}
   151  		sort.Strings(keys)
   152  
   153  		// Write each record to a buffer.
   154  		var buf bytes.Buffer
   155  		for _, k := range keys {
   156  			rec, err := formatPAXRecord(k, paxHdrs[k])
   157  			if err != nil {
   158  				return err
   159  			}
   160  			buf.WriteString(rec)
   161  		}
   162  
   163  		// Write the extended header file.
   164  		var name string
   165  		var flag byte
   166  		if isGlobal {
   167  			name = "GlobalHead.0.0"
   168  			flag = TypeXGlobalHeader
   169  		} else {
   170  			dir, file := path.Split(realName)
   171  			name = path.Join(dir, "PaxHeaders.0", file)
   172  			flag = TypeXHeader
   173  		}
   174  		data := buf.String()
   175  		if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
   176  			return err // Global headers return here
   177  		}
   178  	}
   179  
   180  	// Pack the main header.
   181  	var f formatter // Ignore errors since they are expected
   182  	fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) }
   183  	blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal)
   184  	blk.SetFormat(FormatPAX)
   185  	if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
   186  		return err
   187  	}
   188  
   189  	// Write the sparse map and setup the sparse writer if necessary.
   190  	if len(spd) > 0 {
   191  		// Use tw.curr since the sparse map is accounted for in hdr.Size.
   192  		if _, err := tw.curr.Write(spb); err != nil {
   193  			return err
   194  		}
   195  		tw.curr = &sparseFileWriter{tw.curr, spd, 0}
   196  	}
   197  	return nil
   198  }
   199  
   200  func (tw *Writer) writeGNUHeader(hdr *Header) error {
   201  	// Use long-link files if Name or Linkname exceeds the field size.
   202  	const longName = "././@LongLink"
   203  	if len(hdr.Name) > nameSize {
   204  		data := hdr.Name + "\x00"
   205  		if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil {
   206  			return err
   207  		}
   208  	}
   209  	if len(hdr.Linkname) > nameSize {
   210  		data := hdr.Linkname + "\x00"
   211  		if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil {
   212  			return err
   213  		}
   214  	}
   215  
   216  	// Pack the main header.
   217  	var f formatter // Ignore errors since they are expected
   218  	var spd sparseDatas
   219  	var spb []byte
   220  	blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric)
   221  	if !hdr.AccessTime.IsZero() {
   222  		f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix())
   223  	}
   224  	if !hdr.ChangeTime.IsZero() {
   225  		f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix())
   226  	}
   227  	if hdr.Typeflag == TypeGNUSparse {
   228  		sph := append([]SparseEntry{}, hdr.SparseHoles...) // Copy sparse map
   229  		sph = alignSparseEntries(sph, hdr.Size)
   230  		spd = invertSparseEntries(sph, hdr.Size)
   231  
   232  		// Format the sparse map.
   233  		formatSPD := func(sp sparseDatas, sa sparseArray) sparseDatas {
   234  			for i := 0; len(sp) > 0 && i < sa.MaxEntries(); i++ {
   235  				f.formatNumeric(sa.Entry(i).Offset(), sp[0].Offset)
   236  				f.formatNumeric(sa.Entry(i).Length(), sp[0].Length)
   237  				sp = sp[1:]
   238  			}
   239  			if len(sp) > 0 {
   240  				sa.IsExtended()[0] = 1
   241  			}
   242  			return sp
   243  		}
   244  		sp2 := formatSPD(spd, blk.GNU().Sparse())
   245  		for len(sp2) > 0 {
   246  			var spHdr block
   247  			sp2 = formatSPD(sp2, spHdr.Sparse())
   248  			spb = append(spb, spHdr[:]...)
   249  		}
   250  
   251  		// Update size fields in the header block.
   252  		realSize := hdr.Size
   253  		hdr.Size = 0 // Encoded size; does not account for encoded sparse map
   254  		for _, s := range spd {
   255  			hdr.Size += s.Length
   256  		}
   257  		copy(blk.V7().Size(), zeroBlock[:]) // Reset field
   258  		f.formatNumeric(blk.V7().Size(), hdr.Size)
   259  		f.formatNumeric(blk.GNU().RealSize(), realSize)
   260  	}
   261  	blk.SetFormat(FormatGNU)
   262  	if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
   263  		return err
   264  	}
   265  
   266  	// Write the extended sparse map and setup the sparse writer if necessary.
   267  	if len(spd) > 0 {
   268  		// Use tw.w since the sparse map is not accounted for in hdr.Size.
   269  		if _, err := tw.w.Write(spb); err != nil {
   270  			return err
   271  		}
   272  		tw.curr = &sparseFileWriter{tw.curr, spd, 0}
   273  	}
   274  	return nil
   275  }
   276  
   277  type (
   278  	stringFormatter func([]byte, string)
   279  	numberFormatter func([]byte, int64)
   280  )
   281  
   282  // templateV7Plus fills out the V7 fields of a block using values from hdr.
   283  // It also fills out fields (uname, gname, devmajor, devminor) that are
   284  // shared in the USTAR, PAX, and GNU formats using the provided formatters.
   285  //
   286  // The block returned is only valid until the next call to
   287  // templateV7Plus or writeRawFile.
   288  func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block {
   289  	tw.blk.Reset()
   290  
   291  	modTime := hdr.ModTime
   292  	if modTime.IsZero() {
   293  		modTime = time.Unix(0, 0)
   294  	}
   295  
   296  	v7 := tw.blk.V7()
   297  	v7.TypeFlag()[0] = hdr.Typeflag
   298  	fmtStr(v7.Name(), hdr.Name)
   299  	fmtStr(v7.LinkName(), hdr.Linkname)
   300  	fmtNum(v7.Mode(), hdr.Mode)
   301  	fmtNum(v7.UID(), int64(hdr.Uid))
   302  	fmtNum(v7.GID(), int64(hdr.Gid))
   303  	fmtNum(v7.Size(), hdr.Size)
   304  	fmtNum(v7.ModTime(), modTime.Unix())
   305  
   306  	ustar := tw.blk.USTAR()
   307  	fmtStr(ustar.UserName(), hdr.Uname)
   308  	fmtStr(ustar.GroupName(), hdr.Gname)
   309  	fmtNum(ustar.DevMajor(), hdr.Devmajor)
   310  	fmtNum(ustar.DevMinor(), hdr.Devminor)
   311  
   312  	return &tw.blk
   313  }
   314  
   315  // writeRawFile writes a minimal file with the given name and flag type.
   316  // It uses format to encode the header format and will write data as the body.
   317  // It uses default values for all of the other fields (as BSD and GNU tar does).
   318  func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error {
   319  	tw.blk.Reset()
   320  
   321  	// Best effort for the filename.
   322  	name = toASCII(name)
   323  	if len(name) > nameSize {
   324  		name = name[:nameSize]
   325  	}
   326  
   327  	var f formatter
   328  	v7 := tw.blk.V7()
   329  	v7.TypeFlag()[0] = flag
   330  	f.formatString(v7.Name(), name)
   331  	f.formatOctal(v7.Mode(), 0)
   332  	f.formatOctal(v7.UID(), 0)
   333  	f.formatOctal(v7.GID(), 0)
   334  	f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB
   335  	f.formatOctal(v7.ModTime(), 0)
   336  	tw.blk.SetFormat(format)
   337  	if f.err != nil {
   338  		return f.err // Only occurs if size condition is violated
   339  	}
   340  
   341  	// Write the header and data.
   342  	if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil {
   343  		return err
   344  	}
   345  	_, err := io.WriteString(tw, data)
   346  	return err
   347  }
   348  
   349  // writeRawHeader writes the value of blk, regardless of its value.
   350  // It sets up the Writer such that it can accept a file of the given size.
   351  // If the flag is a special header-only flag, then the size is treated as zero.
   352  func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error {
   353  	if err := tw.Flush(); err != nil {
   354  		return err
   355  	}
   356  	if _, err := tw.w.Write(blk[:]); err != nil {
   357  		return err
   358  	}
   359  	if isHeaderOnlyType(flag) {
   360  		size = 0
   361  	}
   362  	tw.curr = &regFileWriter{tw.w, size}
   363  	tw.pad = blockPadding(size)
   364  	return nil
   365  }
   366  
   367  // splitUSTARPath splits a path according to USTAR prefix and suffix rules.
   368  // If the path is not splittable, then it will return ("", "", false).
   369  func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
   370  	length := len(name)
   371  	if length <= nameSize || !isASCII(name) {
   372  		return "", "", false
   373  	} else if length > prefixSize+1 {
   374  		length = prefixSize + 1
   375  	} else if name[length-1] == '/' {
   376  		length--
   377  	}
   378  
   379  	i := strings.LastIndex(name[:length], "/")
   380  	nlen := len(name) - i - 1 // nlen is length of suffix
   381  	plen := i                 // plen is length of prefix
   382  	if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
   383  		return "", "", false
   384  	}
   385  	return name[:i], name[i+1:], true
   386  }
   387  
   388  // Write writes to the current file in the tar archive.
   389  // Write returns the error ErrWriteTooLong if more than
   390  // Header.Size bytes are written after WriteHeader.
   391  //
   392  // If the current file is sparse, then the regions marked as a hole
   393  // must be written as NUL-bytes.
   394  //
   395  // Calling Write on special types like TypeLink, TypeSymLink, TypeChar,
   396  // TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless
   397  // of what the Header.Size claims.
   398  func (tw *Writer) Write(b []byte) (int, error) {
   399  	if tw.err != nil {
   400  		return 0, tw.err
   401  	}
   402  	n, err := tw.curr.Write(b)
   403  	if err != nil && err != ErrWriteTooLong {
   404  		tw.err = err
   405  	}
   406  	return n, err
   407  }
   408  
   409  // TODO(dsnet): Export the Writer.FillZeros method to assist in quickly zeroing
   410  // out sections of a file. This is especially useful for efficiently
   411  // skipping over large holes in a sparse file.
   412  
   413  // fillZeros writes n bytes of zeros to the current file,
   414  // returning the number of bytes written.
   415  // If fewer than n bytes are discarded, it returns an non-nil error,
   416  // which may be ErrWriteTooLong if the current file is complete.
   417  func (tw *Writer) fillZeros(n int64) (int64, error) {
   418  	if tw.err != nil {
   419  		return 0, tw.err
   420  	}
   421  	n, err := tw.curr.FillZeros(n)
   422  	if err != nil && err != ErrWriteTooLong {
   423  		tw.err = err
   424  	}
   425  	return n, err
   426  }
   427  
   428  // Close closes the tar archive by flushing the padding, and writing the footer.
   429  // If the current file (from a prior call to WriteHeader) is not fully written,
   430  // then this returns an error.
   431  func (tw *Writer) Close() error {
   432  	if tw.err == ErrWriteAfterClose {
   433  		return nil
   434  	}
   435  	if tw.err != nil {
   436  		return tw.err
   437  	}
   438  
   439  	// Trailer: two zero blocks.
   440  	err := tw.Flush()
   441  	for i := 0; i < 2 && err == nil; i++ {
   442  		_, err = tw.w.Write(zeroBlock[:])
   443  	}
   444  
   445  	// Ensure all future actions are invalid.
   446  	tw.err = ErrWriteAfterClose
   447  	return err // Report IO errors
   448  }
   449  
   450  // regFileWriter is a fileWriter for writing data to a regular file entry.
   451  type regFileWriter struct {
   452  	w  io.Writer // Underlying Writer
   453  	nb int64     // Number of remaining bytes to write
   454  }
   455  
   456  func (fw *regFileWriter) Write(b []byte) (int, error) {
   457  	overwrite := int64(len(b)) > fw.nb
   458  	if overwrite {
   459  		b = b[:fw.nb]
   460  	}
   461  	n, err := fw.w.Write(b)
   462  	fw.nb -= int64(n)
   463  	switch {
   464  	case err != nil:
   465  		return n, err
   466  	case overwrite:
   467  		return n, ErrWriteTooLong
   468  	default:
   469  		return n, nil
   470  	}
   471  }
   472  
   473  func (fw *regFileWriter) FillZeros(n int64) (int64, error) {
   474  	return io.CopyN(fw, zeroReader{}, n)
   475  }
   476  
   477  func (fw regFileWriter) Remaining() int64 {
   478  	return fw.nb
   479  }
   480  
   481  // sparseFileWriter is a fileWriter for writing data to a sparse file entry.
   482  type sparseFileWriter struct {
   483  	fw  fileWriter  // Underlying fileWriter
   484  	sp  sparseDatas // Normalized list of data fragments
   485  	pos int64       // Current position in sparse file
   486  }
   487  
   488  func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
   489  	overwrite := int64(len(b)) > sw.Remaining()
   490  	if overwrite {
   491  		b = b[:sw.Remaining()]
   492  	}
   493  
   494  	b0 := b
   495  	endPos := sw.pos + int64(len(b))
   496  	for endPos > sw.pos && err == nil {
   497  		var nf int // Bytes written in fragment
   498  		dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
   499  		if sw.pos < dataStart { // In a hole fragment
   500  			bf := b[:min(int64(len(b)), dataStart-sw.pos)]
   501  			nf, err = zeroWriter{}.Write(bf)
   502  		} else { // In a data fragment
   503  			bf := b[:min(int64(len(b)), dataEnd-sw.pos)]
   504  			nf, err = sw.fw.Write(bf)
   505  		}
   506  		b = b[nf:]
   507  		sw.pos += int64(nf)
   508  		if sw.pos >= dataEnd && len(sw.sp) > 1 {
   509  			sw.sp = sw.sp[1:] // Ensure last fragment always remains
   510  		}
   511  	}
   512  
   513  	n = len(b0) - len(b)
   514  	switch {
   515  	case err == ErrWriteTooLong:
   516  		return n, errMissData // Not possible; implies bug in validation logic
   517  	case err != nil:
   518  		return n, err
   519  	case sw.Remaining() == 0 && sw.fw.Remaining() > 0:
   520  		return n, errUnrefData // Not possible; implies bug in validation logic
   521  	case overwrite:
   522  		return n, ErrWriteTooLong
   523  	default:
   524  		return n, nil
   525  	}
   526  }
   527  
   528  func (sw *sparseFileWriter) FillZeros(n int64) (int64, error) {
   529  	overwrite := n > sw.Remaining()
   530  	if overwrite {
   531  		n = sw.Remaining()
   532  	}
   533  
   534  	var realFill int64 // Number of real data bytes to fill
   535  	endPos := sw.pos + n
   536  	for endPos > sw.pos {
   537  		var nf int64 // Size of fragment
   538  		dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
   539  		if sw.pos < dataStart { // In a hole fragment
   540  			nf = min(endPos-sw.pos, dataStart-sw.pos)
   541  		} else { // In a data fragment
   542  			nf = min(endPos-sw.pos, dataEnd-sw.pos)
   543  			realFill += nf
   544  		}
   545  		sw.pos += nf
   546  		if sw.pos >= dataEnd && len(sw.sp) > 1 {
   547  			sw.sp = sw.sp[1:] // Ensure last fragment always remains
   548  		}
   549  	}
   550  
   551  	_, err := sw.fw.FillZeros(realFill)
   552  	switch {
   553  	case err == ErrWriteTooLong:
   554  		return n, errMissData // Not possible; implies bug in validation logic
   555  	case err != nil:
   556  		return n, err
   557  	case sw.Remaining() == 0 && sw.fw.Remaining() > 0:
   558  		return n, errUnrefData // Not possible; implies bug in validation logic
   559  	case overwrite:
   560  		return n, ErrWriteTooLong
   561  	default:
   562  		return n, nil
   563  	}
   564  }
   565  
   566  func (sw sparseFileWriter) Remaining() int64 {
   567  	return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
   568  }
   569  
   570  // zeroWriter may only be written with NULs, otherwise it returns errWriteHole.
   571  type zeroWriter struct{}
   572  
   573  func (zeroWriter) Write(b []byte) (int, error) {
   574  	for i, c := range b {
   575  		if c != 0 {
   576  			return i, errWriteHole
   577  		}
   578  	}
   579  	return len(b), nil
   580  }