github.com/Press-One/go-update@v1.0.0/internal/binarydist/patch.go (about)

     1  package binarydist
     2  
     3  import (
     4  	"bytes"
     5  	"compress/bzip2"
     6  	"encoding/binary"
     7  	"errors"
     8  	"io"
     9  	"io/ioutil"
    10  )
    11  
    12  var ErrCorrupt = errors.New("corrupt patch")
    13  
    14  // Patch applies patch to old, according to the bspatch algorithm,
    15  // and writes the result to new.
    16  func Patch(old io.Reader, new io.Writer, patch io.Reader) error {
    17  	var hdr header
    18  	err := binary.Read(patch, signMagLittleEndian{}, &hdr)
    19  	if err != nil {
    20  		return err
    21  	}
    22  	if hdr.Magic != magic {
    23  		return ErrCorrupt
    24  	}
    25  	if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 {
    26  		return ErrCorrupt
    27  	}
    28  
    29  	ctrlbuf := make([]byte, hdr.CtrlLen)
    30  	_, err = io.ReadFull(patch, ctrlbuf)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf))
    35  
    36  	diffbuf := make([]byte, hdr.DiffLen)
    37  	_, err = io.ReadFull(patch, diffbuf)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf))
    42  
    43  	// The entire rest of the file is the extra block.
    44  	epfbz2 := bzip2.NewReader(patch)
    45  
    46  	obuf, err := ioutil.ReadAll(old)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	nbuf := make([]byte, hdr.NewSize)
    52  
    53  	var oldpos, newpos int64
    54  	for newpos < hdr.NewSize {
    55  		var ctrl struct{ Add, Copy, Seek int64 }
    56  		err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl)
    57  		if err != nil {
    58  			return err
    59  		}
    60  
    61  		// Sanity-check
    62  		if newpos+ctrl.Add > hdr.NewSize {
    63  			return ErrCorrupt
    64  		}
    65  
    66  		// Read diff string
    67  		_, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add])
    68  		if err != nil {
    69  			return ErrCorrupt
    70  		}
    71  
    72  		// Add old data to diff string
    73  		for i := int64(0); i < ctrl.Add; i++ {
    74  			if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) {
    75  				nbuf[newpos+i] += obuf[oldpos+i]
    76  			}
    77  		}
    78  
    79  		// Adjust pointers
    80  		newpos += ctrl.Add
    81  		oldpos += ctrl.Add
    82  
    83  		// Sanity-check
    84  		if newpos+ctrl.Copy > hdr.NewSize {
    85  			return ErrCorrupt
    86  		}
    87  
    88  		// Read extra string
    89  		_, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy])
    90  		if err != nil {
    91  			return ErrCorrupt
    92  		}
    93  
    94  		// Adjust pointers
    95  		newpos += ctrl.Copy
    96  		oldpos += ctrl.Seek
    97  	}
    98  
    99  	// Write the new file
   100  	for len(nbuf) > 0 {
   101  		n, err := new.Write(nbuf)
   102  		if err != nil {
   103  			return err
   104  		}
   105  		nbuf = nbuf[n:]
   106  	}
   107  
   108  	return nil
   109  }