diff options
author | Zahoor Mohamed <zahoor@zahoor.in> | 2017-03-23 21:56:06 +0800 |
---|---|---|
committer | Felix Lange <fjl@users.noreply.github.com> | 2017-03-23 21:56:06 +0800 |
commit | 11e7a712f469fb24ddb88ecebcefab6ed8880eb8 (patch) | |
tree | c052776c80475767eb7a038bef99ff784b071ef7 /swarm/api/swarmfs_unix.go | |
parent | 61d2150a0750a554250c3bf090ef994be6c060f0 (diff) | |
download | go-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.go | 266 |
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 +} |