github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/swarm/fuse/swarmfs_unix.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:47</date> 10 //</624342671481114624> 11 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 // 25 // 26 // 27 28 // 29 30 package fuse 31 32 import ( 33 "context" 34 "errors" 35 "fmt" 36 "os" 37 "path/filepath" 38 "strings" 39 "sync" 40 "time" 41 42 "bazil.org/fuse" 43 "bazil.org/fuse/fs" 44 "github.com/ethereum/go-ethereum/common" 45 "github.com/ethereum/go-ethereum/swarm/api" 46 "github.com/ethereum/go-ethereum/swarm/log" 47 ) 48 49 var ( 50 errEmptyMountPoint = errors.New("need non-empty mount point") 51 errNoRelativeMountPoint = errors.New("invalid path for mount point (need absolute path)") 52 errMaxMountCount = errors.New("max FUSE mount count reached") 53 errMountTimeout = errors.New("mount timeout") 54 errAlreadyMounted = errors.New("mount point is already serving") 55 ) 56 57 func isFUSEUnsupportedError(err error) bool { 58 if perr, ok := err.(*os.PathError); ok { 59 return perr.Op == "open" && perr.Path == "/dev/fuse" 60 } 61 return err == fuse.ErrOSXFUSENotFound 62 } 63 64 // 65 type MountInfo struct { 66 MountPoint string 67 StartManifest string 68 LatestManifest string 69 rootDir *SwarmDir 70 fuseConnection *fuse.Conn 71 swarmApi *api.API 72 lock *sync.RWMutex 73 serveClose chan struct{} 74 } 75 76 func NewMountInfo(mhash, mpoint string, sapi *api.API) *MountInfo { 77 log.Debug("swarmfs NewMountInfo", "hash", mhash, "mount point", mpoint) 78 newMountInfo := &MountInfo{ 79 MountPoint: mpoint, 80 StartManifest: mhash, 81 LatestManifest: mhash, 82 rootDir: nil, 83 fuseConnection: nil, 84 swarmApi: sapi, 85 lock: &sync.RWMutex{}, 86 serveClose: make(chan struct{}), 87 } 88 return newMountInfo 89 } 90 91 func (swarmfs *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) { 92 log.Info("swarmfs", "mounting hash", mhash, "mount point", mountpoint) 93 if mountpoint == "" { 94 return nil, errEmptyMountPoint 95 } 96 if !strings.HasPrefix(mountpoint, "/") { 97 return nil, errNoRelativeMountPoint 98 } 99 cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) 100 if err != nil { 101 return nil, err 102 } 103 log.Trace("swarmfs mount", "cleanedMountPoint", cleanedMountPoint) 104 105 swarmfs.swarmFsLock.Lock() 106 defer swarmfs.swarmFsLock.Unlock() 107 108 noOfActiveMounts := len(swarmfs.activeMounts) 109 log.Debug("swarmfs mount", "# active mounts", noOfActiveMounts) 110 if noOfActiveMounts >= maxFuseMounts { 111 return nil, errMaxMountCount 112 } 113 114 if _, ok := swarmfs.activeMounts[cleanedMountPoint]; ok { 115 return nil, errAlreadyMounted 116 } 117 118 log.Trace("swarmfs mount: getting manifest tree") 119 _, manifestEntryMap, err := swarmfs.swarmApi.BuildDirectoryTree(context.TODO(), mhash, true) 120 if err != nil { 121 return nil, err 122 } 123 124 log.Trace("swarmfs mount: building mount info") 125 mi := NewMountInfo(mhash, cleanedMountPoint, swarmfs.swarmApi) 126 127 dirTree := map[string]*SwarmDir{} 128 rootDir := NewSwarmDir("/", mi) 129 log.Trace("swarmfs mount", "rootDir", rootDir) 130 mi.rootDir = rootDir 131 132 log.Trace("swarmfs mount: traversing manifest map") 133 for suffix, entry := range manifestEntryMap { 134 if suffix == "" { // 135 log.Warn("Manifest has an empty-path (default) entry which will be ignored in FUSE mount.") 136 continue 137 } 138 addr := common.Hex2Bytes(entry.Hash) 139 fullpath := "/" + suffix 140 basepath := filepath.Dir(fullpath) 141 parentDir := rootDir 142 dirUntilNow := "" 143 paths := strings.Split(basepath, "/") 144 for i := range paths { 145 if paths[i] != "" { 146 thisDir := paths[i] 147 dirUntilNow = dirUntilNow + "/" + thisDir 148 149 if _, ok := dirTree[dirUntilNow]; !ok { 150 dirTree[dirUntilNow] = NewSwarmDir(dirUntilNow, mi) 151 parentDir.directories = append(parentDir.directories, dirTree[dirUntilNow]) 152 parentDir = dirTree[dirUntilNow] 153 154 } else { 155 parentDir = dirTree[dirUntilNow] 156 } 157 } 158 } 159 thisFile := NewSwarmFile(basepath, filepath.Base(fullpath), mi) 160 thisFile.addr = addr 161 162 parentDir.files = append(parentDir.files, thisFile) 163 } 164 165 fconn, err := fuse.Mount(cleanedMountPoint, fuse.FSName("swarmfs"), fuse.VolumeName(mhash)) 166 if isFUSEUnsupportedError(err) { 167 log.Error("swarmfs error - FUSE not installed", "mountpoint", cleanedMountPoint, "err", err) 168 return nil, err 169 } else if err != nil { 170 fuse.Unmount(cleanedMountPoint) 171 log.Error("swarmfs error mounting swarm manifest", "mountpoint", cleanedMountPoint, "err", err) 172 return nil, err 173 } 174 mi.fuseConnection = fconn 175 176 serverr := make(chan error, 1) 177 go func() { 178 log.Info("swarmfs", "serving hash", mhash, "at", cleanedMountPoint) 179 filesys := &SwarmRoot{root: rootDir} 180 // 181 if err := fs.Serve(fconn, filesys); err != nil { 182 log.Warn("swarmfs could not serve the requested hash", "error", err) 183 serverr <- err 184 } 185 mi.serveClose <- struct{}{} 186 }() 187 188 /* 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 */ 209 210 time.Sleep(2 * time.Second) 211 212 timer := time.NewTimer(mountTimeout) 213 defer timer.Stop() 214 // 215 select { 216 case <-timer.C: 217 log.Warn("swarmfs timed out mounting over FUSE", "mountpoint", cleanedMountPoint, "err", err) 218 err := fuse.Unmount(cleanedMountPoint) 219 if err != nil { 220 return nil, err 221 } 222 return nil, errMountTimeout 223 case err := <-serverr: 224 log.Warn("swarmfs error serving over FUSE", "mountpoint", cleanedMountPoint, "err", err) 225 err = fuse.Unmount(cleanedMountPoint) 226 return nil, err 227 228 case <-fconn.Ready: 229 // 230 // 231 if err := fconn.MountError; err != nil { 232 log.Error("Mounting error from fuse driver: ", "err", err) 233 return nil, err 234 } 235 log.Info("swarmfs now served over FUSE", "manifest", mhash, "mountpoint", cleanedMountPoint) 236 } 237 238 timer.Stop() 239 swarmfs.activeMounts[cleanedMountPoint] = mi 240 return mi, nil 241 } 242 243 func (swarmfs *SwarmFS) Unmount(mountpoint string) (*MountInfo, error) { 244 swarmfs.swarmFsLock.Lock() 245 defer swarmfs.swarmFsLock.Unlock() 246 247 cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) 248 if err != nil { 249 return nil, err 250 } 251 252 mountInfo := swarmfs.activeMounts[cleanedMountPoint] 253 254 if mountInfo == nil || mountInfo.MountPoint != cleanedMountPoint { 255 return nil, fmt.Errorf("swarmfs %s is not mounted", cleanedMountPoint) 256 } 257 err = fuse.Unmount(cleanedMountPoint) 258 if err != nil { 259 err1 := externalUnmount(cleanedMountPoint) 260 if err1 != nil { 261 errStr := fmt.Sprintf("swarmfs unmount error: %v", err) 262 log.Warn(errStr) 263 return nil, err1 264 } 265 } 266 267 err = mountInfo.fuseConnection.Close() 268 if err != nil { 269 return nil, err 270 } 271 delete(swarmfs.activeMounts, cleanedMountPoint) 272 273 <-mountInfo.serveClose 274 275 succString := fmt.Sprintf("swarmfs unmounting %v succeeded", cleanedMountPoint) 276 log.Info(succString) 277 278 return mountInfo, nil 279 } 280 281 func (swarmfs *SwarmFS) Listmounts() []*MountInfo { 282 swarmfs.swarmFsLock.RLock() 283 defer swarmfs.swarmFsLock.RUnlock() 284 rows := make([]*MountInfo, 0, len(swarmfs.activeMounts)) 285 for _, mi := range swarmfs.activeMounts { 286 rows = append(rows, mi) 287 } 288 return rows 289 } 290 291 func (swarmfs *SwarmFS) Stop() bool { 292 for mp := range swarmfs.activeMounts { 293 mountInfo := swarmfs.activeMounts[mp] 294 swarmfs.Unmount(mountInfo.MountPoint) 295 } 296 return true 297 } 298