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