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  }