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