github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/fuse/swarmfs_unix.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 // +build linux darwin freebsd 13 14 package fuse 15 16 import ( 17 "errors" 18 "fmt" 19 "os" 20 "path/filepath" 21 "strings" 22 "sync" 23 "time" 24 25 "bazil.org/fuse" 26 "bazil.org/fuse/fs" 27 "github.com/Sberex/go-sberex/common" 28 "github.com/Sberex/go-sberex/log" 29 "github.com/Sberex/go-sberex/swarm/api" 30 ) 31 32 var ( 33 errEmptyMountPoint = errors.New("need non-empty mount point") 34 errMaxMountCount = errors.New("max FUSE mount count reached") 35 errMountTimeout = errors.New("mount timeout") 36 errAlreadyMounted = errors.New("mount point is already serving") 37 ) 38 39 func isFUSEUnsupportedError(err error) bool { 40 if perr, ok := err.(*os.PathError); ok { 41 return perr.Op == "open" && perr.Path == "/dev/fuse" 42 } 43 return err == fuse.ErrOSXFUSENotFound 44 } 45 46 // information about every active mount 47 type MountInfo struct { 48 MountPoint string 49 StartManifest string 50 LatestManifest string 51 rootDir *SwarmDir 52 fuseConnection *fuse.Conn 53 swarmApi *api.Api 54 lock *sync.RWMutex 55 } 56 57 func NewMountInfo(mhash, mpoint string, sapi *api.Api) *MountInfo { 58 newMountInfo := &MountInfo{ 59 MountPoint: mpoint, 60 StartManifest: mhash, 61 LatestManifest: mhash, 62 rootDir: nil, 63 fuseConnection: nil, 64 swarmApi: sapi, 65 lock: &sync.RWMutex{}, 66 } 67 return newMountInfo 68 } 69 70 func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) { 71 72 if mountpoint == "" { 73 return nil, errEmptyMountPoint 74 } 75 cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) 76 if err != nil { 77 return nil, err 78 } 79 80 self.swarmFsLock.Lock() 81 defer self.swarmFsLock.Unlock() 82 83 noOfActiveMounts := len(self.activeMounts) 84 if noOfActiveMounts >= maxFuseMounts { 85 return nil, errMaxMountCount 86 } 87 88 if _, ok := self.activeMounts[cleanedMountPoint]; ok { 89 return nil, errAlreadyMounted 90 } 91 92 log.Info(fmt.Sprintf("Attempting to mount %s ", cleanedMountPoint)) 93 _, manifestEntryMap, err := self.swarmApi.BuildDirectoryTree(mhash, true) 94 if err != nil { 95 return nil, err 96 } 97 98 mi := NewMountInfo(mhash, cleanedMountPoint, self.swarmApi) 99 100 dirTree := map[string]*SwarmDir{} 101 rootDir := NewSwarmDir("/", mi) 102 dirTree["/"] = rootDir 103 mi.rootDir = rootDir 104 105 for suffix, entry := range manifestEntryMap { 106 key := common.Hex2Bytes(entry.Hash) 107 fullpath := "/" + suffix 108 basepath := filepath.Dir(fullpath) 109 110 parentDir := rootDir 111 dirUntilNow := "" 112 paths := strings.Split(basepath, "/") 113 for i := range paths { 114 if paths[i] != "" { 115 thisDir := paths[i] 116 dirUntilNow = dirUntilNow + "/" + thisDir 117 118 if _, ok := dirTree[dirUntilNow]; !ok { 119 dirTree[dirUntilNow] = NewSwarmDir(dirUntilNow, mi) 120 parentDir.directories = append(parentDir.directories, dirTree[dirUntilNow]) 121 parentDir = dirTree[dirUntilNow] 122 123 } else { 124 parentDir = dirTree[dirUntilNow] 125 } 126 127 } 128 } 129 thisFile := NewSwarmFile(basepath, filepath.Base(fullpath), mi) 130 thisFile.key = key 131 132 parentDir.files = append(parentDir.files, thisFile) 133 } 134 135 fconn, err := fuse.Mount(cleanedMountPoint, fuse.FSName("swarmfs"), fuse.VolumeName(mhash)) 136 if isFUSEUnsupportedError(err) { 137 log.Warn("Fuse not installed", "mountpoint", cleanedMountPoint, "err", err) 138 return nil, err 139 } else if err != nil { 140 fuse.Unmount(cleanedMountPoint) 141 log.Warn("Error mounting swarm manifest", "mountpoint", cleanedMountPoint, "err", err) 142 return nil, err 143 } 144 mi.fuseConnection = fconn 145 146 serverr := make(chan error, 1) 147 go func() { 148 log.Info(fmt.Sprintf("Serving %s at %s", mhash, cleanedMountPoint)) 149 filesys := &SwarmRoot{root: rootDir} 150 if err := fs.Serve(fconn, filesys); err != nil { 151 log.Warn(fmt.Sprintf("Could not Serve SwarmFileSystem error: %v", err)) 152 serverr <- err 153 } 154 155 }() 156 157 // Check if the mount process has an error to report. 158 select { 159 case <-time.After(mountTimeout): 160 fuse.Unmount(cleanedMountPoint) 161 return nil, errMountTimeout 162 163 case err := <-serverr: 164 fuse.Unmount(cleanedMountPoint) 165 log.Warn("Error serving swarm FUSE FS", "mountpoint", cleanedMountPoint, "err", err) 166 return nil, err 167 168 case <-fconn.Ready: 169 log.Info("Now serving swarm FUSE FS", "manifest", mhash, "mountpoint", cleanedMountPoint) 170 } 171 172 self.activeMounts[cleanedMountPoint] = mi 173 return mi, nil 174 } 175 176 func (self *SwarmFS) Unmount(mountpoint string) (*MountInfo, error) { 177 178 self.swarmFsLock.Lock() 179 defer self.swarmFsLock.Unlock() 180 181 cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) 182 if err != nil { 183 return nil, err 184 } 185 186 mountInfo := self.activeMounts[cleanedMountPoint] 187 188 if mountInfo == nil || mountInfo.MountPoint != cleanedMountPoint { 189 return nil, fmt.Errorf("%s is not mounted", cleanedMountPoint) 190 } 191 err = fuse.Unmount(cleanedMountPoint) 192 if err != nil { 193 err1 := externalUnmount(cleanedMountPoint) 194 if err1 != nil { 195 errStr := fmt.Sprintf("UnMount error: %v", err) 196 log.Warn(errStr) 197 return nil, err1 198 } 199 } 200 201 mountInfo.fuseConnection.Close() 202 delete(self.activeMounts, cleanedMountPoint) 203 204 succString := fmt.Sprintf("UnMounting %v succeeded", cleanedMountPoint) 205 log.Info(succString) 206 207 return mountInfo, nil 208 } 209 210 func (self *SwarmFS) Listmounts() []*MountInfo { 211 self.swarmFsLock.RLock() 212 defer self.swarmFsLock.RUnlock() 213 214 rows := make([]*MountInfo, 0, len(self.activeMounts)) 215 for _, mi := range self.activeMounts { 216 rows = append(rows, mi) 217 } 218 return rows 219 } 220 221 func (self *SwarmFS) Stop() bool { 222 for mp := range self.activeMounts { 223 mountInfo := self.activeMounts[mp] 224 self.Unmount(mountInfo.MountPoint) 225 } 226 return true 227 }