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