Source file src/archive/tar/writer.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tar
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"io/fs"
    12  	"path"
    13  	"slices"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  // Writer provides sequential writing of a tar archive.
    19  // [Writer.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  	ReadFrom(io.Reader) (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  // This is unnecessary as the next call to [Writer.WriteHeader] or [Writer.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.logicalRemaining(); nb > 0 {
    56  		return fmt.Errorf("archive/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  	tw.hdr = *hdr // Shallow copy of Header
    74  
    75  	// Avoid usage of the legacy TypeRegA flag, and automatically promote
    76  	// it to use TypeReg or TypeDir.
    77  	if tw.hdr.Typeflag == TypeRegA {
    78  		if strings.HasSuffix(tw.hdr.Name, "/") {
    79  			tw.hdr.Typeflag = TypeDir
    80  		} else {
    81  			tw.hdr.Typeflag = TypeReg
    82  		}
    83  	}
    84  
    85  	// Round ModTime and ignore AccessTime and ChangeTime unless
    86  	// the format is explicitly chosen.
    87  	// This ensures nominal usage of WriteHeader (without specifying the format)
    88  	// does not always result in the PAX format being chosen, which
    89  	// causes a 1KiB increase to every header.
    90  	if tw.hdr.Format == FormatUnknown {
    91  		tw.hdr.ModTime = tw.hdr.ModTime.Round(time.Second)
    92  		tw.hdr.AccessTime = time.Time{}
    93  		tw.hdr.ChangeTime = time.Time{}
    94  	}
    95  
    96  	allowedFormats, paxHdrs, err := tw.hdr.allowedFormats()
    97  	switch {
    98  	case allowedFormats.has(FormatUSTAR):
    99  		tw.err = tw.writeUSTARHeader(&tw.hdr)
   100  		return tw.err
   101  	case allowedFormats.has(FormatPAX):
   102  		tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs)
   103  		return tw.err
   104  	case allowedFormats.has(FormatGNU):
   105  		tw.err = tw.writeGNUHeader(&tw.hdr)
   106  		return tw.err
   107  	default:
   108  		return err // Non-fatal error
   109  	}
   110  }
   111  
   112  func (tw *Writer) writeUSTARHeader(hdr *Header) error {
   113  	// Check if we can use USTAR prefix/suffix splitting.
   114  	var namePrefix string
   115  	if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok {
   116  		namePrefix, hdr.Name = prefix, suffix
   117  	}
   118  
   119  	// Pack the main header.
   120  	var f formatter
   121  	blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
   122  	f.formatString(blk.toUSTAR().prefix(), namePrefix)
   123  	blk.setFormat(FormatUSTAR)
   124  	if f.err != nil {
   125  		return f.err // Should never happen since header is validated
   126  	}
   127  	return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag)
   128  }
   129  
   130  func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
   131  	realName, realSize := hdr.Name, hdr.Size
   132  
   133  	// TODO(dsnet): Re-enable this when adding sparse support.
   134  	// See https://golang.org/issue/22735
   135  	/*
   136  		// Handle sparse files.
   137  		var spd sparseDatas
   138  		var spb []byte
   139  		if len(hdr.SparseHoles) > 0 {
   140  			sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map
   141  			sph = alignSparseEntries(sph, hdr.Size)
   142  			spd = invertSparseEntries(sph, hdr.Size)
   143  
   144  			// Format the sparse map.
   145  			hdr.Size = 0 // Replace with encoded size
   146  			spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n')
   147  			for _, s := range spd {
   148  				hdr.Size += s.Length
   149  				spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n')
   150  				spb = append(strconv.AppendInt(spb, s.Length, 10), '\n')
   151  			}
   152  			pad := blockPadding(int64(len(spb)))
   153  			spb = append(spb, zeroBlock[:pad]...)
   154  			hdr.Size += int64(len(spb)) // Accounts for encoded sparse map
   155  
   156  			// Add and modify appropriate PAX records.
   157  			dir, file := path.Split(realName)
   158  			hdr.Name = path.Join(dir, "GNUSparseFile.0", file)
   159  			paxHdrs[paxGNUSparseMajor] = "1"
   160  			paxHdrs[paxGNUSparseMinor] = "0"
   161  			paxHdrs[paxGNUSparseName] = realName
   162  			paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10)
   163  			paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10)
   164  			delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName
   165  		}
   166  	*/
   167  	_ = realSize
   168  
   169  	// Write PAX records to the output.
   170  	isGlobal := hdr.Typeflag == TypeXGlobalHeader
   171  	if len(paxHdrs) > 0 || isGlobal {
   172  		// Sort keys for deterministic ordering.
   173  		var keys []string
   174  		for k := range paxHdrs {
   175  			keys = append(keys, k)
   176  		}
   177  		slices.Sort(keys)
   178  
   179  		// Write each record to a buffer.
   180  		var buf strings.Builder
   181  		for _, k := range keys {
   182  			rec, err := formatPAXRecord(k, paxHdrs[k])
   183  			if err != nil {
   184  				return err
   185  			}
   186  			buf.WriteString(rec)
   187  		}
   188  
   189  		// Write the extended header file.
   190  		var name string
   191  		var flag byte
   192  		if isGlobal {
   193  			name = realName
   194  			if name == "" {
   195  				name = "GlobalHead.0.0"
   196  			}
   197  			flag = TypeXGlobalHeader
   198  		} else {
   199  			dir, file := path.Split(realName)
   200  			name = path.Join(dir, "PaxHeaders.0", file)
   201  			flag = TypeXHeader
   202  		}
   203  		data := buf.String()
   204  		if len(data) > maxSpecialFileSize {
   205  			return ErrFieldTooLong
   206  		}
   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.toGNU().accessTime(), hdr.AccessTime.Unix())
   259  	}
   260  	if !hdr.ChangeTime.IsZero() {
   261  		f.formatNumeric(blk.toGNU().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.toV7()
   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.toUSTAR()
   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.toV7()
   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  // AddFS adds the files from fs.FS to the archive.
   409  // It walks the directory tree starting at the root of the filesystem
   410  // adding each file to the tar archive while maintaining the directory structure.
   411  func (tw *Writer) AddFS(fsys fs.FS) error {
   412  	return fs.WalkDir(fsys, ".", func(name string, d fs.DirEntry, err error) error {
   413  		if err != nil {
   414  			return err
   415  		}
   416  		if d.IsDir() {
   417  			return nil
   418  		}
   419  		info, err := d.Info()
   420  		if err != nil {
   421  			return err
   422  		}
   423  		// TODO(#49580): Handle symlinks when fs.ReadLinkFS is available.
   424  		if !info.Mode().IsRegular() {
   425  			return errors.New("tar: cannot add non-regular file")
   426  		}
   427  		h, err := FileInfoHeader(info, "")
   428  		if err != nil {
   429  			return err
   430  		}
   431  		h.Name = name
   432  		if err := tw.WriteHeader(h); err != nil {
   433  			return err
   434  		}
   435  		f, err := fsys.Open(name)
   436  		if err != nil {
   437  			return err
   438  		}
   439  		defer f.Close()
   440  		_, err = io.Copy(tw, f)
   441  		return err
   442  	})
   443  }
   444  
   445  // splitUSTARPath splits a path according to USTAR prefix and suffix rules.
   446  // If the path is not splittable, then it will return ("", "", false).
   447  func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
   448  	length := len(name)
   449  	if length <= nameSize || !isASCII(name) {
   450  		return "", "", false
   451  	} else if length > prefixSize+1 {
   452  		length = prefixSize + 1
   453  	} else if name[length-1] == '/' {
   454  		length--
   455  	}
   456  
   457  	i := strings.LastIndex(name[:length], "/")
   458  	nlen := len(name) - i - 1 // nlen is length of suffix
   459  	plen := i                 // plen is length of prefix
   460  	if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
   461  		return "", "", false
   462  	}
   463  	return name[:i], name[i+1:], true
   464  }
   465  
   466  // Write writes to the current file in the tar archive.
   467  // Write returns the error [ErrWriteTooLong] if more than
   468  // Header.Size bytes are written after [Writer.WriteHeader].
   469  //
   470  // Calling Write on special types like [TypeLink], [TypeSymlink], [TypeChar],
   471  // [TypeBlock], [TypeDir], and [TypeFifo] returns (0, [ErrWriteTooLong]) regardless
   472  // of what the [Header.Size] claims.
   473  func (tw *Writer) Write(b []byte) (int, error) {
   474  	if tw.err != nil {
   475  		return 0, tw.err
   476  	}
   477  	n, err := tw.curr.Write(b)
   478  	if err != nil && err != ErrWriteTooLong {
   479  		tw.err = err
   480  	}
   481  	return n, err
   482  }
   483  
   484  // readFrom populates the content of the current file by reading from r.
   485  // The bytes read must match the number of remaining bytes in the current file.
   486  //
   487  // If the current file is sparse and r is an io.ReadSeeker,
   488  // then readFrom uses Seek to skip past holes defined in Header.SparseHoles,
   489  // assuming that skipped regions are all NULs.
   490  // This always reads the last byte to ensure r is the right size.
   491  //
   492  // TODO(dsnet): Re-export this when adding sparse file support.
   493  // See https://golang.org/issue/22735
   494  func (tw *Writer) readFrom(r io.Reader) (int64, error) {
   495  	if tw.err != nil {
   496  		return 0, tw.err
   497  	}
   498  	n, err := tw.curr.ReadFrom(r)
   499  	if err != nil && err != ErrWriteTooLong {
   500  		tw.err = err
   501  	}
   502  	return n, err
   503  }
   504  
   505  // Close closes the tar archive by flushing the padding, and writing the footer.
   506  // If the current file (from a prior call to [Writer.WriteHeader]) is not fully written,
   507  // then this returns an error.
   508  func (tw *Writer) Close() error {
   509  	if tw.err == ErrWriteAfterClose {
   510  		return nil
   511  	}
   512  	if tw.err != nil {
   513  		return tw.err
   514  	}
   515  
   516  	// Trailer: two zero blocks.
   517  	err := tw.Flush()
   518  	for i := 0; i < 2 && err == nil; i++ {
   519  		_, err = tw.w.Write(zeroBlock[:])
   520  	}
   521  
   522  	// Ensure all future actions are invalid.
   523  	tw.err = ErrWriteAfterClose
   524  	return err // Report IO errors
   525  }
   526  
   527  // regFileWriter is a fileWriter for writing data to a regular file entry.
   528  type regFileWriter struct {
   529  	w  io.Writer // Underlying Writer
   530  	nb int64     // Number of remaining bytes to write
   531  }
   532  
   533  func (fw *regFileWriter) Write(b []byte) (n int, err error) {
   534  	overwrite := int64(len(b)) > fw.nb
   535  	if overwrite {
   536  		b = b[:fw.nb]
   537  	}
   538  	if len(b) > 0 {
   539  		n, err = fw.w.Write(b)
   540  		fw.nb -= int64(n)
   541  	}
   542  	switch {
   543  	case err != nil:
   544  		return n, err
   545  	case overwrite:
   546  		return n, ErrWriteTooLong
   547  	default:
   548  		return n, nil
   549  	}
   550  }
   551  
   552  func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
   553  	return io.Copy(struct{ io.Writer }{fw}, r)
   554  }
   555  
   556  // logicalRemaining implements fileState.logicalRemaining.
   557  func (fw regFileWriter) logicalRemaining() int64 {
   558  	return fw.nb
   559  }
   560  
   561  // physicalRemaining implements fileState.physicalRemaining.
   562  func (fw regFileWriter) physicalRemaining() int64 {
   563  	return fw.nb
   564  }
   565  
   566  // sparseFileWriter is a fileWriter for writing data to a sparse file entry.
   567  type sparseFileWriter struct {
   568  	fw  fileWriter  // Underlying fileWriter
   569  	sp  sparseDatas // Normalized list of data fragments
   570  	pos int64       // Current position in sparse file
   571  }
   572  
   573  func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
   574  	overwrite := int64(len(b)) > sw.logicalRemaining()
   575  	if overwrite {
   576  		b = b[:sw.logicalRemaining()]
   577  	}
   578  
   579  	b0 := b
   580  	endPos := sw.pos + int64(len(b))
   581  	for endPos > sw.pos && err == nil {
   582  		var nf int // Bytes written in fragment
   583  		dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
   584  		if sw.pos < dataStart { // In a hole fragment
   585  			bf := b[:min(int64(len(b)), dataStart-sw.pos)]
   586  			nf, err = zeroWriter{}.Write(bf)
   587  		} else { // In a data fragment
   588  			bf := b[:min(int64(len(b)), dataEnd-sw.pos)]
   589  			nf, err = sw.fw.Write(bf)
   590  		}
   591  		b = b[nf:]
   592  		sw.pos += int64(nf)
   593  		if sw.pos >= dataEnd && len(sw.sp) > 1 {
   594  			sw.sp = sw.sp[1:] // Ensure last fragment always remains
   595  		}
   596  	}
   597  
   598  	n = len(b0) - len(b)
   599  	switch {
   600  	case err == ErrWriteTooLong:
   601  		return n, errMissData // Not possible; implies bug in validation logic
   602  	case err != nil:
   603  		return n, err
   604  	case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
   605  		return n, errUnrefData // Not possible; implies bug in validation logic
   606  	case overwrite:
   607  		return n, ErrWriteTooLong
   608  	default:
   609  		return n, nil
   610  	}
   611  }
   612  
   613  func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
   614  	rs, ok := r.(io.ReadSeeker)
   615  	if ok {
   616  		if _, err := rs.Seek(0, io.SeekCurrent); err != nil {
   617  			ok = false // Not all io.Seeker can really seek
   618  		}
   619  	}
   620  	if !ok {
   621  		return io.Copy(struct{ io.Writer }{sw}, r)
   622  	}
   623  
   624  	var readLastByte bool
   625  	pos0 := sw.pos
   626  	for sw.logicalRemaining() > 0 && !readLastByte && err == nil {
   627  		var nf int64 // Size of fragment
   628  		dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
   629  		if sw.pos < dataStart { // In a hole fragment
   630  			nf = dataStart - sw.pos
   631  			if sw.physicalRemaining() == 0 {
   632  				readLastByte = true
   633  				nf--
   634  			}
   635  			_, err = rs.Seek(nf, io.SeekCurrent)
   636  		} else { // In a data fragment
   637  			nf = dataEnd - sw.pos
   638  			nf, err = io.CopyN(sw.fw, rs, nf)
   639  		}
   640  		sw.pos += nf
   641  		if sw.pos >= dataEnd && len(sw.sp) > 1 {
   642  			sw.sp = sw.sp[1:] // Ensure last fragment always remains
   643  		}
   644  	}
   645  
   646  	// If the last fragment is a hole, then seek to 1-byte before EOF, and
   647  	// read a single byte to ensure the file is the right size.
   648  	if readLastByte && err == nil {
   649  		_, err = mustReadFull(rs, []byte{0})
   650  		sw.pos++
   651  	}
   652  
   653  	n = sw.pos - pos0
   654  	switch {
   655  	case err == io.EOF:
   656  		return n, io.ErrUnexpectedEOF
   657  	case err == ErrWriteTooLong:
   658  		return n, errMissData // Not possible; implies bug in validation logic
   659  	case err != nil:
   660  		return n, err
   661  	case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
   662  		return n, errUnrefData // Not possible; implies bug in validation logic
   663  	default:
   664  		return n, ensureEOF(rs)
   665  	}
   666  }
   667  
   668  func (sw sparseFileWriter) logicalRemaining() int64 {
   669  	return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
   670  }
   671  func (sw sparseFileWriter) physicalRemaining() int64 {
   672  	return sw.fw.physicalRemaining()
   673  }
   674  
   675  // zeroWriter may only be written with NULs, otherwise it returns errWriteHole.
   676  type zeroWriter struct{}
   677  
   678  func (zeroWriter) Write(b []byte) (int, error) {
   679  	for i, c := range b {
   680  		if c != 0 {
   681  			return i, errWriteHole
   682  		}
   683  	}
   684  	return len(b), nil
   685  }
   686  
   687  // ensureEOF checks whether r is at EOF, reporting ErrWriteTooLong if not so.
   688  func ensureEOF(r io.Reader) error {
   689  	n, err := tryReadFull(r, []byte{0})
   690  	switch {
   691  	case n > 0:
   692  		return ErrWriteTooLong
   693  	case err == io.EOF:
   694  		return nil
   695  	default:
   696  		return err
   697  	}
   698  }
   699  

View as plain text