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 }