gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/upload.go (about)

     1  package renter
     2  
     3  // upload.go performs basic preprocessing on upload requests and then adds the
     4  // requested files into the repair heap.
     5  //
     6  // TODO: Currently the minimum contracts check is not enforced while testing,
     7  // which means that code is not covered at all. Enabling enforcement during
     8  // testing will probably break a ton of existing tests, which means they will
     9  // all need to be fixed when we do enable it, but we should enable it.
    10  
    11  import (
    12  	"fmt"
    13  	"os"
    14  
    15  	"gitlab.com/NebulousLabs/errors"
    16  
    17  	"gitlab.com/SiaPrime/SiaPrime/build"
    18  	"gitlab.com/SiaPrime/SiaPrime/crypto"
    19  	"gitlab.com/SiaPrime/SiaPrime/modules"
    20  	"gitlab.com/SiaPrime/SiaPrime/modules/renter/siadir"
    21  	"gitlab.com/SiaPrime/SiaPrime/modules/renter/siafile"
    22  )
    23  
    24  var (
    25  	// errUploadDirectory is returned if the user tries to upload a directory.
    26  	errUploadDirectory = errors.New("cannot upload directory")
    27  )
    28  
    29  // Upload instructs the renter to start tracking a file. The renter will
    30  // automatically upload and repair tracked files using a background loop.
    31  func (r *Renter) Upload(up modules.FileUploadParams) error {
    32  	if err := r.tg.Add(); err != nil {
    33  		return err
    34  	}
    35  	defer r.tg.Done()
    36  
    37  	// Check if the file is a directory.
    38  	sourceInfo, err := os.Stat(up.Source)
    39  	if err != nil {
    40  		return errors.AddContext(err, "unable to stat input file")
    41  	}
    42  	if sourceInfo.IsDir() {
    43  		return errUploadDirectory
    44  	}
    45  
    46  	// Check for read access.
    47  	file, err := os.Open(up.Source)
    48  	if err != nil {
    49  		return errors.AddContext(err, "unable to open the source file")
    50  	}
    51  	file.Close()
    52  
    53  	// Delete existing file if overwrite flag is set. Ignore ErrUnknownPath.
    54  	if up.Force {
    55  		if err := r.DeleteFile(up.SiaPath); err != nil && err != siafile.ErrUnknownPath {
    56  			return errors.AddContext(err, "unable to delete existing file")
    57  		}
    58  	}
    59  
    60  	// Fill in any missing upload params with sensible defaults.
    61  	if up.ErasureCode == nil {
    62  		up.ErasureCode, _ = siafile.NewRSSubCode(defaultDataPieces, defaultParityPieces, crypto.SegmentSize)
    63  	}
    64  
    65  	// Check that we have contracts to upload to. We need at least data +
    66  	// parity/2 contracts. NumPieces is equal to data+parity, and min pieces is
    67  	// equal to parity. Therefore (NumPieces+MinPieces)/2 = (data+data+parity)/2
    68  	// = data+parity/2.
    69  	numContracts := len(r.hostContractor.Contracts())
    70  	requiredContracts := (up.ErasureCode.NumPieces() + up.ErasureCode.MinPieces()) / 2
    71  	if numContracts < requiredContracts && build.Release != "testing" {
    72  		return fmt.Errorf("not enough contracts to upload file: got %v, needed %v", numContracts, (up.ErasureCode.NumPieces()+up.ErasureCode.MinPieces())/2)
    73  	}
    74  
    75  	// Create the directory path on disk. Renter directory is already present so
    76  	// only files not in top level directory need to have directories created
    77  	dirSiaPath, err := up.SiaPath.Dir()
    78  	if err != nil {
    79  		return err
    80  	}
    81  	// Try to create the directory. If ErrPathOverload is returned it already exists.
    82  	siaDirEntry, err := r.staticDirSet.NewSiaDir(dirSiaPath)
    83  	if err != siadir.ErrPathOverload && err != nil {
    84  		return errors.AddContext(err, "unable to create sia directory for new file")
    85  	} else if err == nil {
    86  		siaDirEntry.Close()
    87  	}
    88  
    89  	// Create the Siafile and add to renter
    90  	entry, err := r.staticFileSet.NewSiaFile(up, crypto.GenerateSiaKey(crypto.TypeDefaultRenter), uint64(sourceInfo.Size()), sourceInfo.Mode())
    91  	if err != nil {
    92  		return errors.AddContext(err, "could not create a new sia file")
    93  	}
    94  	defer entry.Close()
    95  
    96  	// No need to upload zero-byte files.
    97  	if sourceInfo.Size() == 0 {
    98  		return nil
    99  	}
   100  
   101  	// Bubble the health of the SiaFile directory to ensure the health is
   102  	// updated with the new file
   103  	go r.threadedBubbleMetadata(dirSiaPath)
   104  
   105  	// Create nil maps for offline and goodForRenew to pass in to
   106  	// managedBuildAndPushChunks. These maps are used to determine the health of
   107  	// the file and its chunks. Nil maps will result in the file and its chunks
   108  	// having the worst possible health which is accurate since the file hasn't
   109  	// been uploaded yet
   110  	nilMap := make(map[string]bool)
   111  	// Send the upload to the repair loop.
   112  	hosts := r.managedRefreshHostsAndWorkers()
   113  	r.managedBuildAndPushChunks([]*siafile.SiaFileSetEntry{entry}, hosts, targetUnstuckChunks, nilMap, nilMap)
   114  	select {
   115  	case r.uploadHeap.newUploads <- struct{}{}:
   116  	default:
   117  	}
   118  	return nil
   119  }