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