...
Run Format

Source file src/archive/tar/writer.go

Documentation: archive/tar

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

View as plain text