...

Source file src/pkg/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		"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.USTAR().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 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 string) { 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.GNU().AccessTime(), hdr.AccessTime.Unix())
   254		}
   255		if !hdr.ChangeTime.IsZero() {
   256			f.formatNumeric(blk.GNU().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, string)
   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.V7()
   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.USTAR()
   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 string, 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 = strings.TrimRight(name, "/")
   362	
   363		var f formatter
   364		v7 := tw.blk.V7()
   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	// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
   404	// If the path is not splittable, then it will return ("", "", false).
   405	func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
   406		length := len(name)
   407		if length <= nameSize || !isASCII(name) {
   408			return "", "", false
   409		} else if length > prefixSize+1 {
   410			length = prefixSize + 1
   411		} else if name[length-1] == '/' {
   412			length--
   413		}
   414	
   415		i := strings.LastIndex(name[:length], "/")
   416		nlen := len(name) - i - 1 // nlen is length of suffix
   417		plen := i                 // plen is length of prefix
   418		if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
   419			return "", "", false
   420		}
   421		return name[:i], name[i+1:], true
   422	}
   423	
   424	// Write writes to the current file in the tar archive.
   425	// Write returns the error ErrWriteTooLong if more than
   426	// Header.Size bytes are written after WriteHeader.
   427	//
   428	// Calling Write on special types like TypeLink, TypeSymlink, TypeChar,
   429	// TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless
   430	// of what the Header.Size claims.
   431	func (tw *Writer) Write(b []byte) (int, error) {
   432		if tw.err != nil {
   433			return 0, tw.err
   434		}
   435		n, err := tw.curr.Write(b)
   436		if err != nil && err != ErrWriteTooLong {
   437			tw.err = err
   438		}
   439		return n, err
   440	}
   441	
   442	// readFrom populates the content of the current file by reading from r.
   443	// The bytes read must match the number of remaining bytes in the current file.
   444	//
   445	// If the current file is sparse and r is an io.ReadSeeker,
   446	// then readFrom uses Seek to skip past holes defined in Header.SparseHoles,
   447	// assuming that skipped regions are all NULs.
   448	// This always reads the last byte to ensure r is the right size.
   449	//
   450	// TODO(dsnet): Re-export this when adding sparse file support.
   451	// See https://golang.org/issue/22735
   452	func (tw *Writer) readFrom(r io.Reader) (int64, error) {
   453		if tw.err != nil {
   454			return 0, tw.err
   455		}
   456		n, err := tw.curr.ReadFrom(r)
   457		if err != nil && err != ErrWriteTooLong {
   458			tw.err = err
   459		}
   460		return n, err
   461	}
   462	
   463	// Close closes the tar archive by flushing the padding, and writing the footer.
   464	// If the current file (from a prior call to WriteHeader) is not fully written,
   465	// then this returns an error.
   466	func (tw *Writer) Close() error {
   467		if tw.err == ErrWriteAfterClose {
   468			return nil
   469		}
   470		if tw.err != nil {
   471			return tw.err
   472		}
   473	
   474		// Trailer: two zero blocks.
   475		err := tw.Flush()
   476		for i := 0; i < 2 && err == nil; i++ {
   477			_, err = tw.w.Write(zeroBlock[:])
   478		}
   479	
   480		// Ensure all future actions are invalid.
   481		tw.err = ErrWriteAfterClose
   482		return err // Report IO errors
   483	}
   484	
   485	// regFileWriter is a fileWriter for writing data to a regular file entry.
   486	type regFileWriter struct {
   487		w  io.Writer // Underlying Writer
   488		nb int64     // Number of remaining bytes to write
   489	}
   490	
   491	func (fw *regFileWriter) Write(b []byte) (n int, err error) {
   492		overwrite := int64(len(b)) > fw.nb
   493		if overwrite {
   494			b = b[:fw.nb]
   495		}
   496		if len(b) > 0 {
   497			n, err = fw.w.Write(b)
   498			fw.nb -= int64(n)
   499		}
   500		switch {
   501		case err != nil:
   502			return n, err
   503		case overwrite:
   504			return n, ErrWriteTooLong
   505		default:
   506			return n, nil
   507		}
   508	}
   509	
   510	func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
   511		return io.Copy(struct{ io.Writer }{fw}, r)
   512	}
   513	
   514	func (fw regFileWriter) LogicalRemaining() int64 {
   515		return fw.nb
   516	}
   517	func (fw regFileWriter) PhysicalRemaining() int64 {
   518		return fw.nb
   519	}
   520	
   521	// sparseFileWriter is a fileWriter for writing data to a sparse file entry.
   522	type sparseFileWriter struct {
   523		fw  fileWriter  // Underlying fileWriter
   524		sp  sparseDatas // Normalized list of data fragments
   525		pos int64       // Current position in sparse file
   526	}
   527	
   528	func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
   529		overwrite := int64(len(b)) > sw.LogicalRemaining()
   530		if overwrite {
   531			b = b[:sw.LogicalRemaining()]
   532		}
   533	
   534		b0 := b
   535		endPos := sw.pos + int64(len(b))
   536		for endPos > sw.pos && err == nil {
   537			var nf int // Bytes written in fragment
   538			dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
   539			if sw.pos < dataStart { // In a hole fragment
   540				bf := b[:min(int64(len(b)), dataStart-sw.pos)]
   541				nf, err = zeroWriter{}.Write(bf)
   542			} else { // In a data fragment
   543				bf := b[:min(int64(len(b)), dataEnd-sw.pos)]
   544				nf, err = sw.fw.Write(bf)
   545			}
   546			b = b[nf:]
   547			sw.pos += int64(nf)
   548			if sw.pos >= dataEnd && len(sw.sp) > 1 {
   549				sw.sp = sw.sp[1:] // Ensure last fragment always remains
   550			}
   551		}
   552	
   553		n = len(b0) - len(b)
   554		switch {
   555		case err == ErrWriteTooLong:
   556			return n, errMissData // Not possible; implies bug in validation logic
   557		case err != nil:
   558			return n, err
   559		case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0:
   560			return n, errUnrefData // Not possible; implies bug in validation logic
   561		case overwrite:
   562			return n, ErrWriteTooLong
   563		default:
   564			return n, nil
   565		}
   566	}
   567	
   568	func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
   569		rs, ok := r.(io.ReadSeeker)
   570		if ok {
   571			if _, err := rs.Seek(0, io.SeekCurrent); err != nil {
   572				ok = false // Not all io.Seeker can really seek
   573			}
   574		}
   575		if !ok {
   576			return io.Copy(struct{ io.Writer }{sw}, r)
   577		}
   578	
   579		var readLastByte bool
   580		pos0 := sw.pos
   581		for sw.LogicalRemaining() > 0 && !readLastByte && err == nil {
   582			var nf int64 // Size of fragment
   583			dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
   584			if sw.pos < dataStart { // In a hole fragment
   585				nf = dataStart - sw.pos
   586				if sw.PhysicalRemaining() == 0 {
   587					readLastByte = true
   588					nf--
   589				}
   590				_, err = rs.Seek(nf, io.SeekCurrent)
   591			} else { // In a data fragment
   592				nf = dataEnd - sw.pos
   593				nf, err = io.CopyN(sw.fw, rs, nf)
   594			}
   595			sw.pos += nf
   596			if sw.pos >= dataEnd && len(sw.sp) > 1 {
   597				sw.sp = sw.sp[1:] // Ensure last fragment always remains
   598			}
   599		}
   600	
   601		// If the last fragment is a hole, then seek to 1-byte before EOF, and
   602		// read a single byte to ensure the file is the right size.
   603		if readLastByte && err == nil {
   604			_, err = mustReadFull(rs, []byte{0})
   605			sw.pos++
   606		}
   607	
   608		n = sw.pos - pos0
   609		switch {
   610		case err == io.EOF:
   611			return n, io.ErrUnexpectedEOF
   612		case err == ErrWriteTooLong:
   613			return n, errMissData // Not possible; implies bug in validation logic
   614		case err != nil:
   615			return n, err
   616		case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0:
   617			return n, errUnrefData // Not possible; implies bug in validation logic
   618		default:
   619			return n, ensureEOF(rs)
   620		}
   621	}
   622	
   623	func (sw sparseFileWriter) LogicalRemaining() int64 {
   624		return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
   625	}
   626	func (sw sparseFileWriter) PhysicalRemaining() int64 {
   627		return sw.fw.PhysicalRemaining()
   628	}
   629	
   630	// zeroWriter may only be written with NULs, otherwise it returns errWriteHole.
   631	type zeroWriter struct{}
   632	
   633	func (zeroWriter) Write(b []byte) (int, error) {
   634		for i, c := range b {
   635			if c != 0 {
   636				return i, errWriteHole
   637			}
   638		}
   639		return len(b), nil
   640	}
   641	
   642	// ensureEOF checks whether r is at EOF, reporting ErrWriteTooLong if not so.
   643	func ensureEOF(r io.Reader) error {
   644		n, err := tryReadFull(r, []byte{0})
   645		switch {
   646		case n > 0:
   647			return ErrWriteTooLong
   648		case err == io.EOF:
   649			return nil
   650		default:
   651			return err
   652		}
   653	}
   654	

View as plain text