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  }