...

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

View as plain text