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