github.com/nebulouslabs/sia@v1.3.7/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 you cannot upload a directory using the api, if you want to
     7  // upload a directory you must make 1 api call per file in that directory.
     8  // Perhaps we should extend this endpoint to be able to recursively add files in
     9  // a directory?
    10  //
    11  // TODO: Currently the minimum contracts check is not enforced while testing,
    12  // which means that code is not covered at all. Enabling enforcement during
    13  // testing will probably break a ton of existing tests, which means they will
    14  // all need to be fixed when we do enable it, but we should enable it.
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"os"
    20  
    21  	"github.com/NebulousLabs/Sia/build"
    22  	"github.com/NebulousLabs/Sia/modules"
    23  )
    24  
    25  var (
    26  	// errUploadDirectory is returned if the user tries to upload a directory.
    27  	errUploadDirectory = errors.New("cannot upload directory")
    28  )
    29  
    30  // validateSource verifies that a sourcePath meets the
    31  // requirements for upload.
    32  func validateSource(sourcePath string) error {
    33  	finfo, err := os.Stat(sourcePath)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	if finfo.IsDir() {
    38  		return errUploadDirectory
    39  	}
    40  
    41  	return nil
    42  }
    43  
    44  // Upload instructs the renter to start tracking a file. The renter will
    45  // automatically upload and repair tracked files using a background loop.
    46  func (r *Renter) Upload(up modules.FileUploadParams) error {
    47  	// Enforce nickname rules.
    48  	if err := validateSiapath(up.SiaPath); err != nil {
    49  		return err
    50  	}
    51  	// Enforce source rules.
    52  	if err := validateSource(up.Source); err != nil {
    53  		return err
    54  	}
    55  
    56  	// Check for a nickname conflict.
    57  	lockID := r.mu.RLock()
    58  	_, exists := r.files[up.SiaPath]
    59  	r.mu.RUnlock(lockID)
    60  	if exists {
    61  		return ErrPathOverload
    62  	}
    63  
    64  	// Fill in any missing upload params with sensible defaults.
    65  	fileInfo, err := os.Stat(up.Source)
    66  	if err != nil {
    67  		return err
    68  	}
    69  	if up.ErasureCode == nil {
    70  		up.ErasureCode, _ = NewRSCode(defaultDataPieces, defaultParityPieces)
    71  	}
    72  
    73  	// Check that we have contracts to upload to. We need at least data +
    74  	// parity/2 contracts. NumPieces is equal to data+parity, and min pieces is
    75  	// equal to parity. Therefore (NumPieces+MinPieces)/2 = (data+data+parity)/2
    76  	// = data+parity/2.
    77  	numContracts := len(r.hostContractor.Contracts())
    78  	requiredContracts := (up.ErasureCode.NumPieces() + up.ErasureCode.MinPieces()) / 2
    79  	if numContracts < requiredContracts && build.Release != "testing" {
    80  		return fmt.Errorf("not enough contracts to upload file: got %v, needed %v", numContracts, (up.ErasureCode.NumPieces()+up.ErasureCode.MinPieces())/2)
    81  	}
    82  
    83  	// Create file object.
    84  	f := newFile(up.SiaPath, up.ErasureCode, pieceSize, uint64(fileInfo.Size()))
    85  	f.mode = uint32(fileInfo.Mode())
    86  
    87  	// Add file to renter.
    88  	lockID = r.mu.Lock()
    89  	r.files[up.SiaPath] = f
    90  	r.persist.Tracking[up.SiaPath] = trackedFile{
    91  		RepairPath: up.Source,
    92  	}
    93  	r.saveSync()
    94  	err = r.saveFile(f)
    95  	r.mu.Unlock(lockID)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	// Send the upload to the repair loop.
   101  	hosts := r.managedRefreshHostsAndWorkers()
   102  	id := r.mu.Lock()
   103  	unfinishedChunks := r.buildUnfinishedChunks(f, hosts)
   104  	r.mu.Unlock(id)
   105  	for i := 0; i < len(unfinishedChunks); i++ {
   106  		r.uploadHeap.managedPush(unfinishedChunks[i])
   107  	}
   108  	select {
   109  	case r.uploadHeap.newUploads <- struct{}{}:
   110  	default:
   111  	}
   112  	return nil
   113  }