github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/synchronization/rsync/receive.go (about)

     1  package rsync
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  
    10  	"github.com/mutagen-io/mutagen/pkg/filesystem"
    11  )
    12  
    13  // EnsureValid ensures that ReceiverState's invariants are respected.
    14  func (s *ReceiverState) EnsureValid() error {
    15  	// A nil receiver state is valid.
    16  	if s == nil {
    17  		return nil
    18  	}
    19  
    20  	// We intentionally avoid sanity checking the received byte count for a
    21  	// particular path because the expected byte count is merely an estimate;
    22  	// the actual transmission of the file is always susceptible to concurrent
    23  	// modifications which may yield more bytes transmitted than expected.
    24  
    25  	// Sanity check path counts.
    26  	if s.ReceivedFiles > s.ExpectedFiles {
    27  		return errors.New("too many files received")
    28  	}
    29  
    30  	// Success.
    31  	return nil
    32  }
    33  
    34  // Receiver manages the streaming reception of multiple files. It should be used
    35  // in conjunction with the Transmit function.
    36  type Receiver interface {
    37  	// Receive processes a single message in a transmission stream.
    38  	Receive(*Transmission) error
    39  	// finalize indicates that the transmission stream is completed and that no
    40  	// more messages will be received. This may indicate the successful
    41  	// completion of transmission, but could also indicate that the stream has
    42  	// failed due to an error. In any case, the receiver should use it as an
    43  	// opportunity to close all internal resources. It must be safe to call
    44  	// finalize after an error is returned from Receive.
    45  	finalize() error
    46  }
    47  
    48  // Sinker provides the interface for a receiver to store incoming files.
    49  type Sinker interface {
    50  	// Sink should return a new io.WriteCloser for staging the given path. Each
    51  	// result it returns will be closed before Sink is invoked again.
    52  	Sink(path string) (io.WriteCloser, error)
    53  }
    54  
    55  // emptyReadSeekCloser is an implementation of io.ReadSeekCloser that is empty.
    56  type emptyReadSeekCloser struct {
    57  	*bytes.Reader
    58  }
    59  
    60  // newEmptyReadSeekCloser constructs a new empty io.ReadSeekCloser.
    61  func newEmptyReadSeekCloser() io.ReadSeekCloser {
    62  	return &emptyReadSeekCloser{&bytes.Reader{}}
    63  }
    64  
    65  // Close implements io.Closer for emptyReadSeekCloser.
    66  func (e *emptyReadSeekCloser) Close() error {
    67  	return nil
    68  }
    69  
    70  // receiver is a Receiver implementation that actually writes files to disk.
    71  type receiver struct {
    72  	// root is the file root.
    73  	root string
    74  	// paths is the list of paths to receive.
    75  	paths []string
    76  	// signatures is the list of signatures corresponding to the bases for these
    77  	// paths.
    78  	signatures []*Signature
    79  	// opener is the filesystem opener used to open base files.
    80  	opener *filesystem.Opener
    81  	// sinker is the Sinker to use for staging files.
    82  	sinker Sinker
    83  	// engine is the rsync Engine.
    84  	engine *Engine
    85  	// received is the number of files received.
    86  	received uint64
    87  	// total is the total number of files to receive (the number of paths).
    88  	total uint64
    89  	// finalized indicates whether or not the receiver has been finalized.
    90  	finalized bool
    91  	// burning indicates that the receiver is currently burning operations due
    92  	// to a failed file receiving operation.
    93  	burning bool
    94  	// base is the base for the current file. It should be non-nil if and only
    95  	// if target is non-nil. It should be nil if burning.
    96  	base io.ReadSeekCloser
    97  	// target is the destination for the current file. It should be non-nil if
    98  	// and only if base is non-nil. It should be nil if burning.
    99  	target io.WriteCloser
   100  }
   101  
   102  // NewReceiver creates a new receiver that stores files on disk. It is the
   103  // responsibility of the caller to ensure that the provided signatures are valid
   104  // by invoking their EnsureValid method. In order for the receiver to perform
   105  // efficiently, paths should be passed in depth-first traversal order.
   106  func NewReceiver(root string, paths []string, signatures []*Signature, sinker Sinker) (Receiver, error) {
   107  	// Ensure that the receiving request is sane.
   108  	if len(paths) != len(signatures) {
   109  		return nil, errors.New("number of paths does not match number of signatures")
   110  	}
   111  
   112  	// Create the receiver.
   113  	return &receiver{
   114  		root:       root,
   115  		paths:      paths,
   116  		signatures: signatures,
   117  		opener:     filesystem.NewOpener(root),
   118  		sinker:     sinker,
   119  		engine:     NewEngine(),
   120  		total:      uint64(len(paths)),
   121  	}, nil
   122  }
   123  
   124  // Receive processes incoming messages by storing files to disk.
   125  func (r *receiver) Receive(transmission *Transmission) error {
   126  	// Check that we haven't been finalized.
   127  	if r.finalized {
   128  		panic("receive called on finalized receiver")
   129  	}
   130  
   131  	// Make sure that we're not seeing a transmission after receiving all files.
   132  	// If we are, it's a terminal error.
   133  	if r.received == r.total {
   134  		return errors.New("unexpected file transmission")
   135  	}
   136  
   137  	// Check if we need to skip this transmission due to burning.
   138  	skip := r.burning
   139  
   140  	// Check if this is a done transmission.
   141  	if transmission.Done {
   142  		// TODO: The transmission may have error information here. Should we
   143  		// expose that to whatever is doing the file sinking? It doesn't matter
   144  		// for our application since we have independent hash validation, but it
   145  		// might be useful for some cases.
   146  
   147  		// Close out base and target if they're open, because we're done with
   148  		// this file. If they're not open, and we're not burning, it means that
   149  		// we have an empty file. Since we won't have opened any sink for the
   150  		// file (no operations came in for it), open one quickly and close it.
   151  		// Since we're already at the end of the stream for this file, there's
   152  		// no need to start burning operations if this fails.
   153  		if r.base != nil {
   154  			r.base.Close()
   155  			r.base = nil
   156  			r.target.Close()
   157  			r.target = nil
   158  		} else if !r.burning {
   159  			if target, _ := r.sinker.Sink(r.paths[r.received]); target != nil {
   160  				target.Close()
   161  			}
   162  		}
   163  
   164  		// Update the received count.
   165  		r.received++
   166  
   167  		// Reset burning status.
   168  		r.burning = false
   169  
   170  		// Skip the transmission (since it doesn't contain any operation).
   171  		skip = true
   172  	}
   173  
   174  	// Skip the transmission if necessary, either due to burning or the fact
   175  	// that it's a done transmission (or both).
   176  	if skip {
   177  		return nil
   178  	}
   179  
   180  	// Extract the signature for this file.
   181  	signature := r.signatures[r.received]
   182  
   183  	// Check if we are starting a new file stream and need to open the base and
   184  	// target.
   185  	if r.base == nil {
   186  		// Extract the path.
   187  		path := r.paths[r.received]
   188  
   189  		// Open the base. If the signature is a zero value, then we just use an
   190  		// empty base. If it's not, then we need to try to open the base. If
   191  		// that fails, then we need to burn this file stream, but it's not a
   192  		// terminal error.
   193  		if signature.isEmpty() {
   194  			r.base = newEmptyReadSeekCloser()
   195  		} else if base, _, err := r.opener.OpenFile(path); err != nil {
   196  			r.burning = true
   197  			return nil
   198  		} else {
   199  			r.base = base
   200  		}
   201  
   202  		// Create a sink. If that fails, then we need to close out the base and
   203  		// burn this file stream, but it's not a terminal error.
   204  		if target, err := r.sinker.Sink(path); err != nil {
   205  			r.base.Close()
   206  			r.base = nil
   207  			r.burning = true
   208  			return nil
   209  		} else {
   210  			r.target = target
   211  		}
   212  	}
   213  
   214  	// Apply the operation. If that fails, then we need to close out the base,
   215  	// target, and burn this file stream, but it's not a terminal error.
   216  	if err := r.engine.Patch(r.target, r.base, signature, transmission.Operation); err != nil {
   217  		r.base.Close()
   218  		r.base = nil
   219  		r.target.Close()
   220  		r.target = nil
   221  		r.burning = true
   222  		return nil
   223  	}
   224  
   225  	// Success.
   226  	return nil
   227  }
   228  
   229  // finalize aborts reception (if still in-progress) closes any open receiver
   230  // resources.
   231  func (r *receiver) finalize() error {
   232  	// Watch for double finalization.
   233  	if r.finalized {
   234  		return errors.New("receiver finalized multiple times")
   235  	}
   236  
   237  	// Close any open internal resources.
   238  	if r.base != nil {
   239  		r.base.Close()
   240  		r.base = nil
   241  		r.target.Close()
   242  		r.target = nil
   243  	}
   244  
   245  	// Close the file opener.
   246  	r.opener.Close()
   247  
   248  	// Mark the receiver as finalized.
   249  	r.finalized = true
   250  
   251  	// Success.
   252  	return nil
   253  }
   254  
   255  // Monitor is the interface that monitors must implement to capture state
   256  // information from a monitoring receiver. The state object provided to this
   257  // function must not be modified or retained. When the monitoring receiver is
   258  // finalized, the Monitor callback will receive a nil state value.
   259  type Monitor func(*ReceiverState) error
   260  
   261  // monitoringReceiver is a Receiver implementation that can invoke a callback
   262  // with information about the status of transmission.
   263  type monitoringReceiver struct {
   264  	// receiver is the underlying receiver.
   265  	receiver Receiver
   266  	// paths is the list of paths the receiver is expecting.
   267  	paths []string
   268  	// signatures are the signatures corresponding to paths.
   269  	signatures []*Signature
   270  	// monitor is the monitoring callback.
   271  	monitor Monitor
   272  	// startOfFile tracks whether or not the next transmission in the stream is
   273  	// expected to coincide with the start of a new file.
   274  	startOfFile bool
   275  	// state is the current receiver state.
   276  	state *ReceiverState
   277  }
   278  
   279  // NewMonitoringReceiver wraps a receiver and provides monitoring information
   280  // via a callback.
   281  func NewMonitoringReceiver(receiver Receiver, paths []string, signatures []*Signature, monitor Monitor) Receiver {
   282  	// Verify that the path and signature counts match.
   283  	if len(paths) != len(signatures) {
   284  		panic("path count does not match signature count")
   285  	}
   286  
   287  	// Create the receiver.
   288  	return &monitoringReceiver{
   289  		receiver:    receiver,
   290  		paths:       paths,
   291  		signatures:  signatures,
   292  		monitor:     monitor,
   293  		startOfFile: true,
   294  		state: &ReceiverState{
   295  			ExpectedFiles: uint64(len(paths)),
   296  		},
   297  	}
   298  }
   299  
   300  // Receive forwards messages to its underlying receiver and performs status
   301  // updates by invoking the specified monitor.
   302  func (r *monitoringReceiver) Receive(transmission *Transmission) error {
   303  	// Make sure that we're not seeing a transmission after receiving all files.
   304  	// If we are, then it's a terminal error.
   305  	if r.state.ReceivedFiles == r.state.ExpectedFiles {
   306  		return errors.New("unexpected file transmission")
   307  	}
   308  
   309  	// Forward the transmission to the underlying receiver.
   310  	if err := r.receiver.Receive(transmission); err != nil {
   311  		return err
   312  	}
   313  
   314  	// If we're at the start of a new file, then compute the path and reset the
   315  	// per-file statistics.
   316  	if r.startOfFile {
   317  		r.state.Path = r.paths[r.state.ReceivedFiles]
   318  		r.state.ReceivedSize = 0
   319  		r.state.ExpectedSize = transmission.ExpectedSize
   320  	}
   321  
   322  	// Compute the amount of data contained in this transmission.
   323  	var dataSize uint64
   324  	if !transmission.Done {
   325  		if d := len(transmission.Operation.Data); d > 0 {
   326  			dataSize = uint64(d)
   327  		} else {
   328  			signature := r.signatures[r.state.ReceivedFiles]
   329  			if transmission.Operation.Start+transmission.Operation.Count == uint64(len(signature.Hashes)) {
   330  				dataSize += (transmission.Operation.Count - 1) * signature.BlockSize
   331  				dataSize += signature.LastBlockSize
   332  			} else {
   333  				dataSize += transmission.Operation.Count * signature.BlockSize
   334  			}
   335  		}
   336  	}
   337  
   338  	// Update received data statistics.
   339  	r.state.ReceivedSize += dataSize
   340  	r.state.TotalReceivedSize += dataSize
   341  
   342  	// Provide the updated state to the monitor if relevant.
   343  	if !transmission.Done || r.startOfFile {
   344  		if err := r.monitor(r.state); err != nil {
   345  			return fmt.Errorf("unable to send receiver state: %w", err)
   346  		}
   347  	}
   348  
   349  	// If we're at the end of transmissions for the current file, then update
   350  	// the received file count.
   351  	if transmission.Done {
   352  		r.state.ReceivedFiles++
   353  	}
   354  
   355  	// Update stream position tracking.
   356  	r.startOfFile = transmission.Done
   357  
   358  	// Success.
   359  	return nil
   360  }
   361  
   362  // finalize invokes finalize on the underlying receiver. It also performs a
   363  // final empty status update, though it doesn't check for an error when doing
   364  // so.
   365  func (r *monitoringReceiver) finalize() error {
   366  	// Perform a final status update. We don't bother checking for an error
   367  	// because it's inconsequential at this point.
   368  	r.monitor(nil)
   369  
   370  	// Invoke finalize on the underlying receiver.
   371  	return r.receiver.finalize()
   372  }
   373  
   374  // preemptableReceiver is a Receiver implementation that provides preemption
   375  // facilities.
   376  type preemptableReceiver struct {
   377  	// ctx is the context in which the receiver is receiving.
   378  	ctx context.Context
   379  	// receiver is the underlying receiver.
   380  	receiver Receiver
   381  }
   382  
   383  // NewPreemptableReceiver wraps a receiver and aborts on Receive if the
   384  // specified context has been cancelled.
   385  func NewPreemptableReceiver(ctx context.Context, receiver Receiver) Receiver {
   386  	return &preemptableReceiver{
   387  		ctx:      ctx,
   388  		receiver: receiver,
   389  	}
   390  }
   391  
   392  // Receive performs a check for preemption, aborting if the receiver has been
   393  // preempted. If no preemption has occurred, the transmission is forwarded to
   394  // the underlying receiver.
   395  func (r *preemptableReceiver) Receive(transmission *Transmission) error {
   396  	// Check for preemption in a non-blocking fashion.
   397  	select {
   398  	case <-r.ctx.Done():
   399  		return errors.New("reception cancelled")
   400  	default:
   401  	}
   402  
   403  	// Forward the transmission.
   404  	return r.receiver.Receive(transmission)
   405  }
   406  
   407  // finalize invokes finalize on the underlying receiver.
   408  func (r *preemptableReceiver) finalize() error {
   409  	return r.receiver.finalize()
   410  }
   411  
   412  // Encoder is the interface used by an encoding receiver to forward
   413  // transmissions, usually across a network.
   414  type Encoder interface {
   415  	// Encode encodes and transmits a transmission. The provided transmission
   416  	// will never be nil. The transmission passed to the encoder may be re-used
   417  	// and modified, so the encoder should not hold on to the transmission
   418  	// between calls (it should either transmit it or fully copy it if
   419  	// transmission is going to be delayed).
   420  	Encode(*Transmission) error
   421  	// Finalize is called when the transmission stream is finished. The Encoder
   422  	// can use this call to close any underlying transmission resources.
   423  	Finalize() error
   424  }
   425  
   426  // encodingReceiver is a Receiver implementation that encodes messages to an
   427  // arbitrary encoder.
   428  type encodingReceiver struct {
   429  	// encoder is the Encoder to use for encoding messages.
   430  	encoder Encoder
   431  	// finalized indicates whether or not the receiver has been finalized.
   432  	finalized bool
   433  }
   434  
   435  // NewEncodingReceiver creates a new receiver that handles messages by encoding
   436  // them with the specified Encoder. It is designed to be used with
   437  // DecodeToReceiver.
   438  func NewEncodingReceiver(encoder Encoder) Receiver {
   439  	return &encodingReceiver{
   440  		encoder: encoder,
   441  	}
   442  }
   443  
   444  // Receive encodes the specified transmission using the underlying encoder.
   445  func (r *encodingReceiver) Receive(transmission *Transmission) error {
   446  	// Encode the transmission.
   447  	if err := r.encoder.Encode(transmission); err != nil {
   448  		return fmt.Errorf("unable to encode transmission: %w", err)
   449  	}
   450  
   451  	// Success.
   452  	return nil
   453  }
   454  
   455  // finalize finalizes the encoding receiver, which means that it calls Finalize
   456  // on its underlying Encoder.
   457  func (r *encodingReceiver) finalize() error {
   458  	// Watch for double finalization.
   459  	if r.finalized {
   460  		return errors.New("receiver finalized multiple times")
   461  	}
   462  
   463  	// Mark ourselves as finalized
   464  	r.finalized = true
   465  
   466  	// Finalize the encoder.
   467  	if err := r.encoder.Finalize(); err != nil {
   468  		return fmt.Errorf("unable to finalize encoder: %w", err)
   469  	}
   470  
   471  	// Success.
   472  	return nil
   473  }
   474  
   475  // Decoder is the interface used by DecodeToReceiver to receive transmissions,
   476  // usually across a network.
   477  type Decoder interface {
   478  	// Decoder decodes a transmission encoded by an encoder. The transmission
   479  	// should be decoded into the specified Transmission object, which will be a
   480  	// non-nil zero-valued Transmission object. The decoder is *not* responsible
   481  	// for validating that the transmission is valid before returning it.
   482  	// TODO: We should really elaborate on the semantics of Decoder, in
   483  	// particular how it is allowed to re-use existing allocations within the
   484  	// Transmission object.
   485  	Decode(*Transmission) error
   486  	// Finalize is called when decoding is finished. The Decoder can use this
   487  	// call to close any underlying transmission resources.
   488  	Finalize() error
   489  }
   490  
   491  // DecodeToReceiver decodes messages from the specified Decoder and forwards
   492  // them to the specified receiver. It must be passed the number of files to be
   493  // received so that it knows when forwarding is complete. It is designed to be
   494  // used with an encoding receiver, such as that returned by NewEncodingReceiver.
   495  // It finalizes the provided receiver before returning.
   496  func DecodeToReceiver(decoder Decoder, count uint64, receiver Receiver) error {
   497  	// Allocate the transmission object that we'll use to receive into.
   498  	transmission := &Transmission{}
   499  
   500  	// Loop until we've seen all files come in.
   501  	for count > 0 {
   502  		// Loop, decode, and forward until we see a done message.
   503  		for {
   504  			// Receive the next message.
   505  			transmission.resetToZeroMaintainingCapacity()
   506  			if err := decoder.Decode(transmission); err != nil {
   507  				decoder.Finalize()
   508  				receiver.finalize()
   509  				return fmt.Errorf("unable to decode transmission: %w", err)
   510  			}
   511  
   512  			// Validate the transmission.
   513  			if err := transmission.EnsureValid(); err != nil {
   514  				decoder.Finalize()
   515  				receiver.finalize()
   516  				return fmt.Errorf("invalid transmission received: %w", err)
   517  			}
   518  
   519  			// Forward the message.
   520  			if err := receiver.Receive(transmission); err != nil {
   521  				decoder.Finalize()
   522  				receiver.finalize()
   523  				return fmt.Errorf("unable to forward message to receiver: %w", err)
   524  			}
   525  
   526  			// If the message indicates completion, we're done receiving
   527  			// messages for this file.
   528  			if transmission.Done {
   529  				break
   530  			}
   531  		}
   532  
   533  		// Update the count.
   534  		count--
   535  	}
   536  
   537  	// Ensure that the decoder is finalized.
   538  	if err := decoder.Finalize(); err != nil {
   539  		receiver.finalize()
   540  		return fmt.Errorf("unable to finalize decoder: %w", err)
   541  	}
   542  
   543  	// Ensure that the receiver is finalized.
   544  	if err := receiver.finalize(); err != nil {
   545  		return fmt.Errorf("unable to finalize receiver: %w", err)
   546  	}
   547  
   548  	// Done.
   549  	return nil
   550  }