github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/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/filepath" 26 "sync" 27 28 "github.com/atheioschain/go-atheios/common" 29 "github.com/atheioschain/go-atheios/logger" 30 "github.com/atheioschain/go-atheios/logger/glog" 31 "github.com/atheioschain/go-atheios/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 dpa store 46 // TODO: localpath should point to a manifest 47 func (self *FileSystem) Upload(lpath, index string) (string, error) { 48 var list []*manifestTrieEntry 49 localpath, err := filepath.Abs(filepath.Clean(lpath)) 50 if err != nil { 51 return "", err 52 } 53 54 f, err := os.Open(localpath) 55 if err != nil { 56 return "", err 57 } 58 stat, err := f.Stat() 59 if err != nil { 60 return "", err 61 } 62 63 var start int 64 if stat.IsDir() { 65 start = len(localpath) 66 glog.V(logger.Debug).Infof("uploading '%s'", localpath) 67 err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { 68 if (err == nil) && !info.IsDir() { 69 //fmt.Printf("lp %s path %s\n", localpath, path) 70 if len(path) <= start { 71 return fmt.Errorf("Path is too short") 72 } 73 if path[:start] != localpath { 74 return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) 75 } 76 entry := &manifestTrieEntry{ 77 Path: filepath.ToSlash(path), 78 } 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 := &manifestTrieEntry{ 96 Path: filepath.ToSlash(localpath), 97 } 98 list = append(list, entry) 99 } 100 101 cnt := len(list) 102 errors := make([]error, cnt) 103 done := make(chan bool, maxParallelFiles) 104 dcnt := 0 105 awg := &sync.WaitGroup{} 106 107 for i, entry := range list { 108 if i >= dcnt+maxParallelFiles { 109 <-done 110 dcnt++ 111 } 112 awg.Add(1) 113 go func(i int, entry *manifestTrieEntry, done chan bool) { 114 f, err := os.Open(entry.Path) 115 if err == nil { 116 stat, _ := f.Stat() 117 var hash storage.Key 118 wg := &sync.WaitGroup{} 119 hash, err = self.api.dpa.Store(f, stat.Size(), wg, nil) 120 if hash != nil { 121 list[i].Hash = hash.String() 122 } 123 wg.Wait() 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 dpa: self.api.dpa, 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 := &manifestTrieEntry{ 158 Path: "", 159 Hash: entry.Hash, 160 ContentType: entry.ContentType, 161 } 162 trie.addEntry(ientry, quitC) 163 } 164 trie.addEntry(entry, quitC) 165 } 166 167 err2 := trie.recalcAndStore() 168 var hs string 169 if err2 == nil { 170 hs = trie.hash.String() 171 } 172 awg.Wait() 173 return hs, err2 174 } 175 176 // Download replicates the manifest path structure on the local filesystem 177 // under localpath 178 func (self *FileSystem) Download(bzzpath, localpath string) error { 179 lpath, err := filepath.Abs(filepath.Clean(localpath)) 180 if err != nil { 181 return err 182 } 183 err = os.MkdirAll(lpath, os.ModePerm) 184 if err != nil { 185 return err 186 } 187 188 //resolving host and port 189 key, _, path, err := self.api.parseAndResolve(bzzpath, true) 190 if err != nil { 191 return err 192 } 193 194 if len(path) > 0 { 195 path += "/" 196 } 197 198 quitC := make(chan bool) 199 trie, err := loadManifest(self.api.dpa, key, quitC) 200 if err != nil { 201 glog.V(logger.Warn).Infof("fs.Download: loadManifestTrie error: %v", err) 202 return err 203 } 204 205 type downloadListEntry struct { 206 key storage.Key 207 path string 208 } 209 210 var list []*downloadListEntry 211 var mde error 212 213 prevPath := lpath 214 err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { 215 glog.V(logger.Detail).Infof("fs.Download: %#v", entry) 216 217 key = common.Hex2Bytes(entry.Hash) 218 path := lpath + "/" + suffix 219 dir := filepath.Dir(path) 220 if dir != prevPath { 221 mde = os.MkdirAll(dir, os.ModePerm) 222 prevPath = dir 223 } 224 if (mde == nil) && (path != dir+"/") { 225 list = append(list, &downloadListEntry{key: key, path: path}) 226 } 227 }) 228 if err != nil { 229 return err 230 } 231 232 wg := sync.WaitGroup{} 233 errC := make(chan error) 234 done := make(chan bool, maxParallelFiles) 235 for i, entry := range list { 236 select { 237 case done <- true: 238 wg.Add(1) 239 case <-quitC: 240 return fmt.Errorf("aborted") 241 } 242 go func(i int, entry *downloadListEntry) { 243 defer wg.Done() 244 err := retrieveToFile(quitC, self.api.dpa, entry.key, entry.path) 245 if err != nil { 246 select { 247 case errC <- err: 248 case <-quitC: 249 } 250 return 251 } 252 <-done 253 }(i, entry) 254 } 255 go func() { 256 wg.Wait() 257 close(errC) 258 }() 259 select { 260 case err = <-errC: 261 return err 262 case <-quitC: 263 return fmt.Errorf("aborted") 264 } 265 } 266 267 func retrieveToFile(quitC chan bool, dpa *storage.DPA, key storage.Key, path string) error { 268 f, err := os.Create(path) // TODO: path separators 269 if err != nil { 270 return err 271 } 272 reader := dpa.Retrieve(key) 273 writer := bufio.NewWriter(f) 274 size, err := reader.Size(quitC) 275 if err != nil { 276 return err 277 } 278 if _, err = io.CopyN(writer, reader, size); err != nil { 279 return err 280 } 281 if err := writer.Flush(); err != nil { 282 return err 283 } 284 return f.Close() 285 }