writer.mx raw

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