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