github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/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 "SiaPrime/build" 22 "SiaPrime/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 }