github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/hellofs/hello_fs.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package hellofs
    16  
    17  import (
    18  	"context"
    19  	"io"
    20  	"os"
    21  	"strings"
    22  
    23  	"github.com/scaleoutsean/fusego"
    24  	"github.com/scaleoutsean/fusego/fuseops"
    25  	"github.com/scaleoutsean/fusego/fuseutil"
    26  	"github.com/jacobsa/timeutil"
    27  )
    28  
    29  // Create a file system with a fixed structure that looks like this:
    30  //
    31  //     hello
    32  //     dir/
    33  //         world
    34  //
    35  // Each file contains the string "Hello, world!".
    36  func NewHelloFS(clock timeutil.Clock) (fuse.Server, error) {
    37  	fs := &helloFS{
    38  		Clock: clock,
    39  	}
    40  
    41  	return fuseutil.NewFileSystemServer(fs), nil
    42  }
    43  
    44  type helloFS struct {
    45  	fuseutil.NotImplementedFileSystem
    46  
    47  	Clock timeutil.Clock
    48  }
    49  
    50  const (
    51  	rootInode fuseops.InodeID = fuseops.RootInodeID + iota
    52  	helloInode
    53  	dirInode
    54  	worldInode
    55  )
    56  
    57  type inodeInfo struct {
    58  	attributes fuseops.InodeAttributes
    59  
    60  	// File or directory?
    61  	dir bool
    62  
    63  	// For directories, children.
    64  	children []fuseutil.Dirent
    65  }
    66  
    67  // We have a fixed directory structure.
    68  var gInodeInfo = map[fuseops.InodeID]inodeInfo{
    69  	// root
    70  	rootInode: inodeInfo{
    71  		attributes: fuseops.InodeAttributes{
    72  			Nlink: 1,
    73  			Mode:  0555 | os.ModeDir,
    74  		},
    75  		dir: true,
    76  		children: []fuseutil.Dirent{
    77  			fuseutil.Dirent{
    78  				Offset: 1,
    79  				Inode:  helloInode,
    80  				Name:   "hello",
    81  				Type:   fuseutil.DT_File,
    82  			},
    83  			fuseutil.Dirent{
    84  				Offset: 2,
    85  				Inode:  dirInode,
    86  				Name:   "dir",
    87  				Type:   fuseutil.DT_Directory,
    88  			},
    89  		},
    90  	},
    91  
    92  	// hello
    93  	helloInode: inodeInfo{
    94  		attributes: fuseops.InodeAttributes{
    95  			Nlink: 1,
    96  			Mode:  0444,
    97  			Size:  uint64(len("Hello, world!")),
    98  		},
    99  	},
   100  
   101  	// dir
   102  	dirInode: inodeInfo{
   103  		attributes: fuseops.InodeAttributes{
   104  			Nlink: 1,
   105  			Mode:  0555 | os.ModeDir,
   106  		},
   107  		dir: true,
   108  		children: []fuseutil.Dirent{
   109  			fuseutil.Dirent{
   110  				Offset: 1,
   111  				Inode:  worldInode,
   112  				Name:   "world",
   113  				Type:   fuseutil.DT_File,
   114  			},
   115  		},
   116  	},
   117  
   118  	// world
   119  	worldInode: inodeInfo{
   120  		attributes: fuseops.InodeAttributes{
   121  			Nlink: 1,
   122  			Mode:  0444,
   123  			Size:  uint64(len("Hello, world!")),
   124  		},
   125  	},
   126  }
   127  
   128  func findChildInode(
   129  	name string,
   130  	children []fuseutil.Dirent) (fuseops.InodeID, error) {
   131  	for _, e := range children {
   132  		if e.Name == name {
   133  			return e.Inode, nil
   134  		}
   135  	}
   136  
   137  	return 0, fuse.ENOENT
   138  }
   139  
   140  func (fs *helloFS) patchAttributes(
   141  	attr *fuseops.InodeAttributes) {
   142  	now := fs.Clock.Now()
   143  	attr.Atime = now
   144  	attr.Mtime = now
   145  	attr.Crtime = now
   146  }
   147  
   148  func (fs *helloFS) StatFS(
   149  	ctx context.Context,
   150  	op *fuseops.StatFSOp) error {
   151  	return nil
   152  }
   153  
   154  func (fs *helloFS) LookUpInode(
   155  	ctx context.Context,
   156  	op *fuseops.LookUpInodeOp) error {
   157  	// Find the info for the parent.
   158  	parentInfo, ok := gInodeInfo[op.Parent]
   159  	if !ok {
   160  		return fuse.ENOENT
   161  	}
   162  
   163  	// Find the child within the parent.
   164  	childInode, err := findChildInode(op.Name, parentInfo.children)
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	// Copy over information.
   170  	op.Entry.Child = childInode
   171  	op.Entry.Attributes = gInodeInfo[childInode].attributes
   172  
   173  	// Patch attributes.
   174  	fs.patchAttributes(&op.Entry.Attributes)
   175  
   176  	return nil
   177  }
   178  
   179  func (fs *helloFS) GetInodeAttributes(
   180  	ctx context.Context,
   181  	op *fuseops.GetInodeAttributesOp) error {
   182  	// Find the info for this inode.
   183  	info, ok := gInodeInfo[op.Inode]
   184  	if !ok {
   185  		return fuse.ENOENT
   186  	}
   187  
   188  	// Copy over its attributes.
   189  	op.Attributes = info.attributes
   190  
   191  	// Patch attributes.
   192  	fs.patchAttributes(&op.Attributes)
   193  
   194  	return nil
   195  }
   196  
   197  func (fs *helloFS) OpenDir(
   198  	ctx context.Context,
   199  	op *fuseops.OpenDirOp) error {
   200  	// Allow opening any directory.
   201  	return nil
   202  }
   203  
   204  func (fs *helloFS) ReadDir(
   205  	ctx context.Context,
   206  	op *fuseops.ReadDirOp) error {
   207  	// Find the info for this inode.
   208  	info, ok := gInodeInfo[op.Inode]
   209  	if !ok {
   210  		return fuse.ENOENT
   211  	}
   212  
   213  	if !info.dir {
   214  		return fuse.EIO
   215  	}
   216  
   217  	entries := info.children
   218  
   219  	// Grab the range of interest.
   220  	if op.Offset > fuseops.DirOffset(len(entries)) {
   221  		return fuse.EIO
   222  	}
   223  
   224  	entries = entries[op.Offset:]
   225  
   226  	// Resume at the specified offset into the array.
   227  	for _, e := range entries {
   228  		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
   229  		if n == 0 {
   230  			break
   231  		}
   232  
   233  		op.BytesRead += n
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  func (fs *helloFS) OpenFile(
   240  	ctx context.Context,
   241  	op *fuseops.OpenFileOp) error {
   242  	// Allow opening any file.
   243  	return nil
   244  }
   245  
   246  func (fs *helloFS) ReadFile(
   247  	ctx context.Context,
   248  	op *fuseops.ReadFileOp) error {
   249  	// Let io.ReaderAt deal with the semantics.
   250  	reader := strings.NewReader("Hello, world!")
   251  
   252  	var err error
   253  	op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
   254  
   255  	// Special case: FUSE doesn't expect us to return io.EOF.
   256  	if err == io.EOF {
   257  		return nil
   258  	}
   259  
   260  	return err
   261  }