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  }