aboutsummaryrefslogtreecommitdiffstats
path: root/swarm/api/swarmfs_unix.go
diff options
context:
space:
mode:
authorZahoor Mohamed <zahoor@zahoor.in>2017-03-23 21:56:06 +0800
committerFelix Lange <fjl@users.noreply.github.com>2017-03-23 21:56:06 +0800
commit11e7a712f469fb24ddb88ecebcefab6ed8880eb8 (patch)
treec052776c80475767eb7a038bef99ff784b071ef7 /swarm/api/swarmfs_unix.go
parent61d2150a0750a554250c3bf090ef994be6c060f0 (diff)
downloadgo-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.tar
go-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.tar.gz
go-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.tar.bz2
go-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.tar.lz
go-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.tar.xz
go-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.tar.zst
go-tangerine-11e7a712f469fb24ddb88ecebcefab6ed8880eb8.zip
swarm/api: support mounting manifests via FUSE (#3690)
Diffstat (limited to 'swarm/api/swarmfs_unix.go')
-rw-r--r--swarm/api/swarmfs_unix.go266
1 files changed, 266 insertions, 0 deletions
diff --git a/swarm/api/swarmfs_unix.go b/swarm/api/swarmfs_unix.go
new file mode 100644
index 000000000..ae0216e1d
--- /dev/null
+++ b/swarm/api/swarmfs_unix.go
@@ -0,0 +1,266 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build linux darwin
+
+package api
+
+import (
+ "path/filepath"
+ "fmt"
+ "strings"
+ "time"
+ "github.com/ethereum/go-ethereum/swarm/storage"
+ "bazil.org/fuse"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/common"
+ "bazil.org/fuse/fs"
+ "sync"
+)
+
+
+var (
+ inode uint64 = 1
+ inodeLock sync.RWMutex
+)
+
+// information about every active mount
+type MountInfo struct {
+ mountPoint string
+ manifestHash string
+ resolvedKey storage.Key
+ rootDir *Dir
+ fuseConnection *fuse.Conn
+}
+
+// Inode numbers need to be unique, they are used for caching inside fuse
+func NewInode() uint64 {
+ inodeLock.Lock()
+ defer inodeLock.Unlock()
+ inode += 1
+ return inode
+}
+
+
+
+func (self *SwarmFS) Mount(mhash, mountpoint string) (string, error) {
+
+ self.activeLock.Lock()
+ defer self.activeLock.Unlock()
+
+ noOfActiveMounts := len(self.activeMounts)
+ if noOfActiveMounts >= maxFuseMounts {
+ err := fmt.Errorf("Max mount count reached. Cannot mount %s ", mountpoint)
+ log.Warn(err.Error())
+ return err.Error(), err
+ }
+
+ cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint))
+ if err != nil {
+ return err.Error(), err
+ }
+
+ if _, ok := self.activeMounts[cleanedMountPoint]; ok {
+ err := fmt.Errorf("Mountpoint %s already mounted.", cleanedMountPoint)
+ log.Warn(err.Error())
+ return err.Error(), err
+ }
+
+ log.Info(fmt.Sprintf("Attempting to mount %s ", cleanedMountPoint))
+ key, _, path, err := self.swarmApi.parseAndResolve(mhash, true)
+ if err != nil {
+ errStr := fmt.Sprintf("Could not resolve %s : %v", mhash, err)
+ log.Warn(errStr)
+ return errStr, err
+ }
+
+ if len(path) > 0 {
+ path += "/"
+ }
+
+ quitC := make(chan bool)
+ trie, err := loadManifest(self.swarmApi.dpa, key, quitC)
+ if err != nil {
+ errStr := fmt.Sprintf("fs.Download: loadManifestTrie error: %v", err)
+ log.Warn(errStr)
+ return errStr, err
+ }
+
+ dirTree := map[string]*Dir{}
+
+ rootDir := &Dir{
+ inode: NewInode(),
+ name: "root",
+ directories: nil,
+ files: nil,
+ }
+ dirTree["root"] = rootDir
+
+ err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) {
+
+ key = common.Hex2Bytes(entry.Hash)
+ fullpath := "/" + suffix
+ basepath := filepath.Dir(fullpath)
+ filename := filepath.Base(fullpath)
+
+ parentDir := rootDir
+ dirUntilNow := ""
+ paths := strings.Split(basepath, "/")
+ for i := range paths {
+ if paths[i] != "" {
+ thisDir := paths[i]
+ dirUntilNow = dirUntilNow + "/" + thisDir
+
+ if _, ok := dirTree[dirUntilNow]; !ok {
+ dirTree[dirUntilNow] = &Dir{
+ inode: NewInode(),
+ name: thisDir,
+ path: dirUntilNow,
+ directories: nil,
+ files: nil,
+ }
+ parentDir.directories = append(parentDir.directories, dirTree[dirUntilNow])
+ parentDir = dirTree[dirUntilNow]
+
+ } else {
+ parentDir = dirTree[dirUntilNow]
+ }
+
+ }
+ }
+ thisFile := &File{
+ inode: NewInode(),
+ name: filename,
+ path: fullpath,
+ key: key,
+ swarmApi: self.swarmApi,
+ }
+ parentDir.files = append(parentDir.files, thisFile)
+ })
+
+ fconn, err := fuse.Mount(cleanedMountPoint, fuse.FSName("swarmfs"), fuse.VolumeName(mhash))
+ if err != nil {
+ fuse.Unmount(cleanedMountPoint)
+ errStr := fmt.Sprintf("Mounting %s encountered error: %v", cleanedMountPoint, err)
+ log.Warn(errStr)
+ return errStr, err
+ }
+
+ mounterr := make(chan error, 1)
+ go func() {
+ log.Info(fmt.Sprintf("Serving %s at %s", mhash, cleanedMountPoint))
+ filesys := &FS{root: rootDir}
+ if err := fs.Serve(fconn, filesys); err != nil {
+ log.Warn(fmt.Sprintf("Could not Serve FS error: %v", err))
+ }
+ }()
+
+ // Check if the mount process has an error to report.
+ select {
+
+ case <-time.After(mountTimeout):
+ err := fmt.Errorf("Mounting %s timed out.", cleanedMountPoint)
+ log.Warn(err.Error())
+ return err.Error(), err
+
+ case err := <-mounterr:
+ errStr := fmt.Sprintf("Mounting %s encountered error: %v", cleanedMountPoint, err)
+ log.Warn(errStr)
+ return errStr, err
+
+ case <-fconn.Ready:
+ log.Debug(fmt.Sprintf("Mounting connection succeeded for : %v", cleanedMountPoint))
+ }
+
+
+
+ //Assemble and Store the mount information for future use
+ mountInformation := &MountInfo{
+ mountPoint: cleanedMountPoint,
+ manifestHash: mhash,
+ resolvedKey: key,
+ rootDir: rootDir,
+ fuseConnection: fconn,
+ }
+ self.activeMounts[cleanedMountPoint] = mountInformation
+
+ succString := fmt.Sprintf("Mounting successful for %s", cleanedMountPoint)
+ log.Info(succString)
+
+ return succString, nil
+}
+
+func (self *SwarmFS) Unmount(mountpoint string) (string, error) {
+
+ self.activeLock.Lock()
+ defer self.activeLock.Unlock()
+
+ cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint))
+ if err != nil {
+ return err.Error(), err
+ }
+
+ // Get the mount information based on the mountpoint argument
+ mountInfo := self.activeMounts[cleanedMountPoint]
+
+
+ if mountInfo == nil || mountInfo.mountPoint != cleanedMountPoint {
+ err := fmt.Errorf("Could not find mount information for %s ", cleanedMountPoint)
+ log.Warn(err.Error())
+ return err.Error(), err
+ }
+
+ err = fuse.Unmount(cleanedMountPoint)
+ if err != nil {
+ //TODO: try forceful unmount if normal unmount fails
+ errStr := fmt.Sprintf("UnMount error: %v", err)
+ log.Warn(errStr)
+ return errStr, err
+ }
+
+ mountInfo.fuseConnection.Close()
+
+ //remove the mount information from the active map
+ delete(self.activeMounts, cleanedMountPoint)
+
+ succString := fmt.Sprintf("UnMounting %v succeeded", cleanedMountPoint)
+ log.Info(succString)
+ return succString, nil
+}
+
+func (self *SwarmFS) Listmounts() (string, error) {
+
+ self.activeLock.RLock()
+ defer self.activeLock.RUnlock()
+
+ var rows []string
+ for mp := range self.activeMounts {
+ mountInfo := self.activeMounts[mp]
+ rows = append(rows, fmt.Sprintf("Swarm Root: %s, Mount Point: %s ", mountInfo.manifestHash, mountInfo.mountPoint))
+ }
+
+ return strings.Join(rows, "\n"), nil
+}
+
+func (self *SwarmFS) Stop() bool {
+
+ for mp := range self.activeMounts {
+ mountInfo := self.activeMounts[mp]
+ self.Unmount(mountInfo.mountPoint)
+ }
+
+ return true
+}