github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/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 19:16:43</date> 10 //</624450113137283072> 11 12 13 //+构建Linux Darwin Freebsd 14 15 package fuse 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "os" 22 "path/filepath" 23 "strings" 24 "sync" 25 "time" 26 27 "bazil.org/fuse" 28 "bazil.org/fuse/fs" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/swarm/api" 31 "github.com/ethereum/go-ethereum/swarm/log" 32 ) 33 34 var ( 35 errEmptyMountPoint = errors.New("need non-empty mount point") 36 errNoRelativeMountPoint = errors.New("invalid path for mount point (need absolute path)") 37 errMaxMountCount = errors.New("max FUSE mount count reached") 38 errMountTimeout = errors.New("mount timeout") 39 errAlreadyMounted = errors.New("mount point is already serving") 40 ) 41 42 func isFUSEUnsupportedError(err error) bool { 43 if perr, ok := err.(*os.PathError); ok { 44 return perr.Op == "open" && perr.Path == "/dev/fuse" 45 } 46 return err == fuse.ErrOSXFUSENotFound 47 } 48 49 //mountinfo包含有关每个活动装载的信息 50 type MountInfo struct { 51 MountPoint string 52 StartManifest string 53 LatestManifest string 54 rootDir *SwarmDir 55 fuseConnection *fuse.Conn 56 swarmApi *api.API 57 lock *sync.RWMutex 58 serveClose chan struct{} 59 } 60 61 func NewMountInfo(mhash, mpoint string, sapi *api.API) *MountInfo { 62 log.Debug("swarmfs NewMountInfo", "hash", mhash, "mount point", mpoint) 63 newMountInfo := &MountInfo{ 64 MountPoint: mpoint, 65 StartManifest: mhash, 66 LatestManifest: mhash, 67 rootDir: nil, 68 fuseConnection: nil, 69 swarmApi: sapi, 70 lock: &sync.RWMutex{}, 71 serveClose: make(chan struct{}), 72 } 73 return newMountInfo 74 } 75 76 func (swarmfs *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) { 77 log.Info("swarmfs", "mounting hash", mhash, "mount point", mountpoint) 78 if mountpoint == "" { 79 return nil, errEmptyMountPoint 80 } 81 if !strings.HasPrefix(mountpoint, "/") { 82 return nil, errNoRelativeMountPoint 83 } 84 cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) 85 if err != nil { 86 return nil, err 87 } 88 log.Trace("swarmfs mount", "cleanedMountPoint", cleanedMountPoint) 89 90 swarmfs.swarmFsLock.Lock() 91 defer swarmfs.swarmFsLock.Unlock() 92 93 noOfActiveMounts := len(swarmfs.activeMounts) 94 log.Debug("swarmfs mount", "# active mounts", noOfActiveMounts) 95 if noOfActiveMounts >= maxFuseMounts { 96 return nil, errMaxMountCount 97 } 98 99 if _, ok := swarmfs.activeMounts[cleanedMountPoint]; ok { 100 return nil, errAlreadyMounted 101 } 102 103 log.Trace("swarmfs mount: getting manifest tree") 104 _, manifestEntryMap, err := swarmfs.swarmApi.BuildDirectoryTree(context.TODO(), mhash, true) 105 if err != nil { 106 return nil, err 107 } 108 109 log.Trace("swarmfs mount: building mount info") 110 mi := NewMountInfo(mhash, cleanedMountPoint, swarmfs.swarmApi) 111 112 dirTree := map[string]*SwarmDir{} 113 rootDir := NewSwarmDir("/", mi) 114 log.Trace("swarmfs mount", "rootDir", rootDir) 115 mi.rootDir = rootDir 116 117 log.Trace("swarmfs mount: traversing manifest map") 118 for suffix, entry := range manifestEntryMap { 119 if suffix == "" { //空后缀表示文件没有名称,即这是清单中的默认条目。因为我们不能有没有名字的文件,所以让我们忽略这个条目。 120 log.Warn("Manifest has an empty-path (default) entry which will be ignored in FUSE mount.") 121 continue 122 } 123 addr := common.Hex2Bytes(entry.Hash) 124 fullpath := "/" + suffix 125 basepath := filepath.Dir(fullpath) 126 parentDir := rootDir 127 dirUntilNow := "" 128 paths := strings.Split(basepath, "/") 129 for i := range paths { 130 if paths[i] != "" { 131 thisDir := paths[i] 132 dirUntilNow = dirUntilNow + "/" + thisDir 133 134 if _, ok := dirTree[dirUntilNow]; !ok { 135 dirTree[dirUntilNow] = NewSwarmDir(dirUntilNow, mi) 136 parentDir.directories = append(parentDir.directories, dirTree[dirUntilNow]) 137 parentDir = dirTree[dirUntilNow] 138 139 } else { 140 parentDir = dirTree[dirUntilNow] 141 } 142 } 143 } 144 thisFile := NewSwarmFile(basepath, filepath.Base(fullpath), mi) 145 thisFile.addr = addr 146 147 parentDir.files = append(parentDir.files, thisFile) 148 } 149 150 fconn, err := fuse.Mount(cleanedMountPoint, fuse.FSName("swarmfs"), fuse.VolumeName(mhash)) 151 if isFUSEUnsupportedError(err) { 152 log.Error("swarmfs error - FUSE not installed", "mountpoint", cleanedMountPoint, "err", err) 153 return nil, err 154 } else if err != nil { 155 fuse.Unmount(cleanedMountPoint) 156 log.Error("swarmfs error mounting swarm manifest", "mountpoint", cleanedMountPoint, "err", err) 157 return nil, err 158 } 159 mi.fuseConnection = fconn 160 161 serverr := make(chan error, 1) 162 go func() { 163 log.Info("swarmfs", "serving hash", mhash, "at", cleanedMountPoint) 164 filesys := &SwarmRoot{root: rootDir} 165 //开始为实际的文件系统提供服务;请参阅下面的注释 166 if err := fs.Serve(fconn, filesys); err != nil { 167 log.Warn("swarmfs could not serve the requested hash", "error", err) 168 serverr <- err 169 } 170 mi.serveClose <- struct{}{} 171 }() 172 173 /* 174 重要提示:fs.serve功能为阻塞; 175 通过调用 176 ATTR在每个swarmfile上运行,创建文件inode; 177 专门调用swarm的lazysactionreader.size()来设置文件大小。 178 179 这可能需要一些时间,而且如果我们访问fuse文件系统 180 太早了,我们可以使测试死锁。目前的假设是 181 此时,fuse驱动程序没有完成初始化文件系统。 182 183 过早地访问文件不仅会使测试死锁,而且会锁定访问 184 完全的fuse文件,导致操作系统级别的资源被阻塞。 185 即使是一个简单的'ls/tmp/testdir/testmountdir'也可能在shell中死锁。 186 187 目前的解决方法是等待一段时间,给操作系统足够的时间来初始化 188 Fuse文件系统。在测试过程中,这似乎解决了这个问题。 189 190 但应注意的是,这可能只是一种影响, 191 以及由于某些竞争条件而阻塞访问的其他原因导致的死锁 192 (在bazil.org库和/或swarmroot、swarmdir和swarmfile实现中导致) 193 **/ 194 195 time.Sleep(2 * time.Second) 196 197 timer := time.NewTimer(mountTimeout) 198 defer timer.Stop() 199 //检查装载过程是否有要报告的错误。 200 select { 201 case <-timer.C: 202 log.Warn("swarmfs timed out mounting over FUSE", "mountpoint", cleanedMountPoint, "err", err) 203 err := fuse.Unmount(cleanedMountPoint) 204 if err != nil { 205 return nil, err 206 } 207 return nil, errMountTimeout 208 case err := <-serverr: 209 log.Warn("swarmfs error serving over FUSE", "mountpoint", cleanedMountPoint, "err", err) 210 err = fuse.Unmount(cleanedMountPoint) 211 return nil, err 212 213 case <-fconn.Ready: 214 //这表示来自保险丝的实际安装点。安装调用已就绪; 215 //虽然fs.serve中的文件系统实际上是完全建立起来的,但它并不表示 216 if err := fconn.MountError; err != nil { 217 log.Error("Mounting error from fuse driver: ", "err", err) 218 return nil, err 219 } 220 log.Info("swarmfs now served over FUSE", "manifest", mhash, "mountpoint", cleanedMountPoint) 221 } 222 223 timer.Stop() 224 swarmfs.activeMounts[cleanedMountPoint] = mi 225 return mi, nil 226 } 227 228 func (swarmfs *SwarmFS) Unmount(mountpoint string) (*MountInfo, error) { 229 swarmfs.swarmFsLock.Lock() 230 defer swarmfs.swarmFsLock.Unlock() 231 232 cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) 233 if err != nil { 234 return nil, err 235 } 236 237 mountInfo := swarmfs.activeMounts[cleanedMountPoint] 238 239 if mountInfo == nil || mountInfo.MountPoint != cleanedMountPoint { 240 return nil, fmt.Errorf("swarmfs %s is not mounted", cleanedMountPoint) 241 } 242 err = fuse.Unmount(cleanedMountPoint) 243 if err != nil { 244 err1 := externalUnmount(cleanedMountPoint) 245 if err1 != nil { 246 errStr := fmt.Sprintf("swarmfs unmount error: %v", err) 247 log.Warn(errStr) 248 return nil, err1 249 } 250 } 251 252 err = mountInfo.fuseConnection.Close() 253 if err != nil { 254 return nil, err 255 } 256 delete(swarmfs.activeMounts, cleanedMountPoint) 257 258 <-mountInfo.serveClose 259 260 succString := fmt.Sprintf("swarmfs unmounting %v succeeded", cleanedMountPoint) 261 log.Info(succString) 262 263 return mountInfo, nil 264 } 265 266 func (swarmfs *SwarmFS) Listmounts() []*MountInfo { 267 swarmfs.swarmFsLock.RLock() 268 defer swarmfs.swarmFsLock.RUnlock() 269 rows := make([]*MountInfo, 0, len(swarmfs.activeMounts)) 270 for _, mi := range swarmfs.activeMounts { 271 rows = append(rows, mi) 272 } 273 return rows 274 } 275 276 func (swarmfs *SwarmFS) Stop() bool { 277 for mp := range swarmfs.activeMounts { 278 mountInfo := swarmfs.activeMounts[mp] 279 swarmfs.Unmount(mountInfo.MountPoint) 280 } 281 return true 282 } 283