github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/api/filesystem.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "bufio" 21 "context" 22 "fmt" 23 "io" 24 "net/http" 25 "os" 26 "path" 27 "path/filepath" 28 "sync" 29 30 "github.com/FusionFoundation/efsn/common" 31 "github.com/FusionFoundation/efsn/swarm/log" 32 "github.com/FusionFoundation/efsn/swarm/storage" 33 ) 34 35 const maxParallelFiles = 5 36 37 type FileSystem struct { 38 api *API 39 } 40 41 func NewFileSystem(api *API) *FileSystem { 42 return &FileSystem{api} 43 } 44 45 // Upload replicates a local directory as a manifest file and uploads it 46 // using FileStore store 47 // This function waits the chunks to be stored. 48 // TODO: localpath should point to a manifest 49 // 50 // DEPRECATED: Use the HTTP API instead 51 func (fs *FileSystem) Upload(lpath, index string, toEncrypt bool) (string, error) { 52 var list []*manifestTrieEntry 53 localpath, err := filepath.Abs(filepath.Clean(lpath)) 54 if err != nil { 55 return "", err 56 } 57 58 f, err := os.Open(localpath) 59 if err != nil { 60 return "", err 61 } 62 stat, err := f.Stat() 63 if err != nil { 64 return "", err 65 } 66 67 var start int 68 if stat.IsDir() { 69 start = len(localpath) 70 log.Debug(fmt.Sprintf("uploading '%s'", localpath)) 71 err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { 72 if (err == nil) && !info.IsDir() { 73 if len(path) <= start { 74 return fmt.Errorf("Path is too short") 75 } 76 if path[:start] != localpath { 77 return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) 78 } 79 entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(path)}, nil) 80 list = append(list, entry) 81 } 82 return err 83 }) 84 if err != nil { 85 return "", err 86 } 87 } else { 88 dir := filepath.Dir(localpath) 89 start = len(dir) 90 if len(localpath) <= start { 91 return "", fmt.Errorf("Path is too short") 92 } 93 if localpath[:start] != dir { 94 return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) 95 } 96 entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(localpath)}, nil) 97 list = append(list, entry) 98 } 99 100 cnt := len(list) 101 errors := make([]error, cnt) 102 done := make(chan bool, maxParallelFiles) 103 dcnt := 0 104 awg := &sync.WaitGroup{} 105 106 for i, entry := range list { 107 if i >= dcnt+maxParallelFiles { 108 <-done 109 dcnt++ 110 } 111 awg.Add(1) 112 go func(i int, entry *manifestTrieEntry, done chan bool) { 113 f, err := os.Open(entry.Path) 114 if err == nil { 115 stat, _ := f.Stat() 116 var hash storage.Address 117 var wait func(context.Context) error 118 ctx := context.TODO() 119 hash, wait, err = fs.api.fileStore.Store(ctx, f, stat.Size(), toEncrypt) 120 if hash != nil { 121 list[i].Hash = hash.Hex() 122 } 123 err = wait(ctx) 124 awg.Done() 125 if err == nil { 126 first512 := make([]byte, 512) 127 fread, _ := f.ReadAt(first512, 0) 128 if fread > 0 { 129 mimeType := http.DetectContentType(first512[:fread]) 130 if filepath.Ext(entry.Path) == ".css" { 131 mimeType = "text/css" 132 } 133 list[i].ContentType = mimeType 134 } 135 } 136 f.Close() 137 } 138 errors[i] = err 139 done <- true 140 }(i, entry, done) 141 } 142 for dcnt < cnt { 143 <-done 144 dcnt++ 145 } 146 147 trie := &manifestTrie{ 148 fileStore: fs.api.fileStore, 149 } 150 quitC := make(chan bool) 151 for i, entry := range list { 152 if errors[i] != nil { 153 return "", errors[i] 154 } 155 entry.Path = RegularSlashes(entry.Path[start:]) 156 if entry.Path == index { 157 ientry := newManifestTrieEntry(&ManifestEntry{ 158 ContentType: entry.ContentType, 159 }, nil) 160 ientry.Hash = entry.Hash 161 trie.addEntry(ientry, quitC) 162 } 163 trie.addEntry(entry, quitC) 164 } 165 166 err2 := trie.recalcAndStore() 167 var hs string 168 if err2 == nil { 169 hs = trie.ref.Hex() 170 } 171 awg.Wait() 172 return hs, err2 173 } 174 175 // Download replicates the manifest basePath structure on the local filesystem 176 // under localpath 177 // 178 // DEPRECATED: Use the HTTP API instead 179 func (fs *FileSystem) Download(bzzpath, localpath string) error { 180 lpath, err := filepath.Abs(filepath.Clean(localpath)) 181 if err != nil { 182 return err 183 } 184 err = os.MkdirAll(lpath, os.ModePerm) 185 if err != nil { 186 return err 187 } 188 189 //resolving host and port 190 uri, err := Parse(path.Join("bzz:/", bzzpath)) 191 if err != nil { 192 return err 193 } 194 addr, err := fs.api.Resolve(context.TODO(), uri.Addr) 195 if err != nil { 196 return err 197 } 198 path := uri.Path 199 200 if len(path) > 0 { 201 path += "/" 202 } 203 204 quitC := make(chan bool) 205 trie, err := loadManifest(context.TODO(), fs.api.fileStore, addr, quitC, NOOPDecrypt) 206 if err != nil { 207 log.Warn(fmt.Sprintf("fs.Download: loadManifestTrie error: %v", err)) 208 return err 209 } 210 211 type downloadListEntry struct { 212 addr storage.Address 213 path string 214 } 215 216 var list []*downloadListEntry 217 var mde error 218 219 prevPath := lpath 220 err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { 221 log.Trace(fmt.Sprintf("fs.Download: %#v", entry)) 222 223 addr = common.Hex2Bytes(entry.Hash) 224 path := lpath + "/" + suffix 225 dir := filepath.Dir(path) 226 if dir != prevPath { 227 mde = os.MkdirAll(dir, os.ModePerm) 228 prevPath = dir 229 } 230 if (mde == nil) && (path != dir+"/") { 231 list = append(list, &downloadListEntry{addr: addr, path: path}) 232 } 233 }) 234 if err != nil { 235 return err 236 } 237 238 wg := sync.WaitGroup{} 239 errC := make(chan error) 240 done := make(chan bool, maxParallelFiles) 241 for i, entry := range list { 242 select { 243 case done <- true: 244 wg.Add(1) 245 case <-quitC: 246 return fmt.Errorf("aborted") 247 } 248 go func(i int, entry *downloadListEntry) { 249 defer wg.Done() 250 err := retrieveToFile(quitC, fs.api.fileStore, entry.addr, entry.path) 251 if err != nil { 252 select { 253 case errC <- err: 254 case <-quitC: 255 } 256 return 257 } 258 <-done 259 }(i, entry) 260 } 261 go func() { 262 wg.Wait() 263 close(errC) 264 }() 265 select { 266 case err = <-errC: 267 return err 268 case <-quitC: 269 return fmt.Errorf("aborted") 270 } 271 } 272 273 func retrieveToFile(quitC chan bool, fileStore *storage.FileStore, addr storage.Address, path string) error { 274 f, err := os.Create(path) // TODO: basePath separators 275 if err != nil { 276 return err 277 } 278 reader, _ := fileStore.Retrieve(context.TODO(), addr) 279 writer := bufio.NewWriter(f) 280 size, err := reader.Size(context.TODO(), quitC) 281 if err != nil { 282 return err 283 } 284 if _, err = io.CopyN(writer, reader, size); err != nil { 285 return err 286 } 287 if err := writer.Flush(); err != nil { 288 return err 289 } 290 return f.Close() 291 }