github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/flushfs/flush_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 flushfs
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"sync"
    22  
    23  	"github.com/scaleoutsean/fusego"
    24  	"github.com/scaleoutsean/fusego/fuseops"
    25  	"github.com/scaleoutsean/fusego/fuseutil"
    26  )
    27  
    28  // Create a file system whose sole contents are a file named "foo" and a
    29  // directory named "bar".
    30  //
    31  // The file may be opened for reading and/or writing. Its initial contents are
    32  // empty. Whenever a flush or fsync is received, the supplied function will be
    33  // called with the current contents of the file and its status returned.
    34  //
    35  // The directory cannot be modified.
    36  func NewFileSystem(
    37  	reportFlush func(string) error,
    38  	reportFsync func(string) error) (fuse.Server, error) {
    39  	fs := &flushFS{
    40  		reportFlush: reportFlush,
    41  		reportFsync: reportFsync,
    42  	}
    43  
    44  	return fuseutil.NewFileSystemServer(fs), nil
    45  }
    46  
    47  const (
    48  	fooID = fuseops.RootInodeID + 1 + iota
    49  	barID
    50  )
    51  
    52  type flushFS struct {
    53  	fuseutil.NotImplementedFileSystem
    54  
    55  	reportFlush func(string) error
    56  	reportFsync func(string) error
    57  
    58  	mu          sync.Mutex
    59  	fooContents []byte // GUARDED_BY(mu)
    60  }
    61  
    62  ////////////////////////////////////////////////////////////////////////
    63  // Helpers
    64  ////////////////////////////////////////////////////////////////////////
    65  
    66  // LOCKS_REQUIRED(fs.mu)
    67  func (fs *flushFS) rootAttributes() fuseops.InodeAttributes {
    68  	return fuseops.InodeAttributes{
    69  		Nlink: 1,
    70  		Mode:  0777 | os.ModeDir,
    71  	}
    72  }
    73  
    74  // LOCKS_REQUIRED(fs.mu)
    75  func (fs *flushFS) fooAttributes() fuseops.InodeAttributes {
    76  	return fuseops.InodeAttributes{
    77  		Nlink: 1,
    78  		Mode:  0777,
    79  		Size:  uint64(len(fs.fooContents)),
    80  	}
    81  }
    82  
    83  // LOCKS_REQUIRED(fs.mu)
    84  func (fs *flushFS) barAttributes() fuseops.InodeAttributes {
    85  	return fuseops.InodeAttributes{
    86  		Nlink: 1,
    87  		Mode:  0777 | os.ModeDir,
    88  	}
    89  }
    90  
    91  // LOCKS_REQUIRED(fs.mu)
    92  func (fs *flushFS) getAttributes(id fuseops.InodeID) (fuseops.InodeAttributes, error) {
    93  	switch id {
    94  	case fuseops.RootInodeID:
    95  		return fs.rootAttributes(), nil
    96  
    97  	case fooID:
    98  		return fs.fooAttributes(), nil
    99  
   100  	case barID:
   101  		return fs.barAttributes(), nil
   102  
   103  	default:
   104  		return fuseops.InodeAttributes{}, fuse.ENOENT
   105  	}
   106  }
   107  
   108  ////////////////////////////////////////////////////////////////////////
   109  // FileSystem methods
   110  ////////////////////////////////////////////////////////////////////////
   111  
   112  func (fs *flushFS) StatFS(
   113  	ctx context.Context,
   114  	op *fuseops.StatFSOp) error {
   115  	return nil
   116  }
   117  
   118  func (fs *flushFS) LookUpInode(
   119  	ctx context.Context,
   120  	op *fuseops.LookUpInodeOp) error {
   121  	fs.mu.Lock()
   122  	defer fs.mu.Unlock()
   123  
   124  	// Sanity check.
   125  	if op.Parent != fuseops.RootInodeID {
   126  		return fuse.ENOENT
   127  	}
   128  
   129  	// Set up the entry.
   130  	switch op.Name {
   131  	case "foo":
   132  		op.Entry = fuseops.ChildInodeEntry{
   133  			Child:      fooID,
   134  			Attributes: fs.fooAttributes(),
   135  		}
   136  
   137  	case "bar":
   138  		op.Entry = fuseops.ChildInodeEntry{
   139  			Child:      barID,
   140  			Attributes: fs.barAttributes(),
   141  		}
   142  
   143  	default:
   144  		return fuse.ENOENT
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (fs *flushFS) GetInodeAttributes(
   151  	ctx context.Context,
   152  	op *fuseops.GetInodeAttributesOp) error {
   153  	fs.mu.Lock()
   154  	defer fs.mu.Unlock()
   155  
   156  	var err error
   157  	op.Attributes, err = fs.getAttributes(op.Inode)
   158  	return err
   159  }
   160  
   161  func (fs *flushFS) SetInodeAttributes(
   162  	ctx context.Context,
   163  	op *fuseops.SetInodeAttributesOp) error {
   164  	fs.mu.Lock()
   165  	defer fs.mu.Unlock()
   166  
   167  	// Ignore any changes and simply return existing attributes.
   168  	var err error
   169  	op.Attributes, err = fs.getAttributes(op.Inode)
   170  	return err
   171  }
   172  
   173  func (fs *flushFS) OpenFile(
   174  	ctx context.Context,
   175  	op *fuseops.OpenFileOp) error {
   176  	fs.mu.Lock()
   177  	defer fs.mu.Unlock()
   178  
   179  	// Sanity check.
   180  	if op.Inode != fooID {
   181  		return fuse.ENOSYS
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  func (fs *flushFS) ReadFile(
   188  	ctx context.Context,
   189  	op *fuseops.ReadFileOp) error {
   190  	fs.mu.Lock()
   191  	defer fs.mu.Unlock()
   192  
   193  	// Ensure the offset is in range.
   194  	if op.Offset > int64(len(fs.fooContents)) {
   195  		return nil
   196  	}
   197  
   198  	// Read what we can.
   199  	op.BytesRead = copy(op.Dst, fs.fooContents[op.Offset:])
   200  
   201  	return nil
   202  }
   203  
   204  func (fs *flushFS) WriteFile(
   205  	ctx context.Context,
   206  	op *fuseops.WriteFileOp) error {
   207  	fs.mu.Lock()
   208  	defer fs.mu.Unlock()
   209  
   210  	// Ensure that the contents slice is long enough.
   211  	newLen := int(op.Offset) + len(op.Data)
   212  	if len(fs.fooContents) < newLen {
   213  		padding := make([]byte, newLen-len(fs.fooContents))
   214  		fs.fooContents = append(fs.fooContents, padding...)
   215  	}
   216  
   217  	// Copy in the data.
   218  	n := copy(fs.fooContents[op.Offset:], op.Data)
   219  
   220  	// Sanity check.
   221  	if n != len(op.Data) {
   222  		panic(fmt.Sprintf("Unexpected short copy: %v", n))
   223  	}
   224  
   225  	return nil
   226  }
   227  
   228  func (fs *flushFS) SyncFile(
   229  	ctx context.Context,
   230  	op *fuseops.SyncFileOp) error {
   231  	fs.mu.Lock()
   232  	defer fs.mu.Unlock()
   233  
   234  	return fs.reportFsync(string(fs.fooContents))
   235  }
   236  
   237  func (fs *flushFS) FlushFile(
   238  	ctx context.Context,
   239  	op *fuseops.FlushFileOp) error {
   240  	fs.mu.Lock()
   241  	defer fs.mu.Unlock()
   242  
   243  	return fs.reportFlush(string(fs.fooContents))
   244  }
   245  
   246  func (fs *flushFS) OpenDir(
   247  	ctx context.Context,
   248  	op *fuseops.OpenDirOp) error {
   249  	fs.mu.Lock()
   250  	defer fs.mu.Unlock()
   251  
   252  	// Sanity check.
   253  	switch op.Inode {
   254  	case fuseops.RootInodeID:
   255  	case barID:
   256  
   257  	default:
   258  		return fuse.ENOENT
   259  	}
   260  
   261  	return nil
   262  }
   263  
   264  func (fs *flushFS) ReadDir(
   265  	ctx context.Context,
   266  	op *fuseops.ReadDirOp) error {
   267  	fs.mu.Lock()
   268  	defer fs.mu.Unlock()
   269  
   270  	// Create the appropriate listing.
   271  	var dirents []fuseutil.Dirent
   272  
   273  	switch op.Inode {
   274  	case fuseops.RootInodeID:
   275  		dirents = []fuseutil.Dirent{
   276  			fuseutil.Dirent{
   277  				Offset: 1,
   278  				Inode:  fooID,
   279  				Name:   "foo",
   280  				Type:   fuseutil.DT_File,
   281  			},
   282  
   283  			fuseutil.Dirent{
   284  				Offset: 2,
   285  				Inode:  barID,
   286  				Name:   "bar",
   287  				Type:   fuseutil.DT_Directory,
   288  			},
   289  		}
   290  
   291  	case barID:
   292  
   293  	default:
   294  		return fmt.Errorf("Unexpected inode: %v", op.Inode)
   295  	}
   296  
   297  	// If the offset is for the end of the listing, we're done. Otherwise we
   298  	// expect it to be for the start.
   299  	switch op.Offset {
   300  	case fuseops.DirOffset(len(dirents)):
   301  		return nil
   302  
   303  	case 0:
   304  
   305  	default:
   306  		return fmt.Errorf("Unexpected offset: %v", op.Offset)
   307  	}
   308  
   309  	// Fill in the listing.
   310  	for _, de := range dirents {
   311  		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], de)
   312  
   313  		// We don't support doing this in anything more than one shot.
   314  		if n == 0 {
   315  			return fmt.Errorf("Couldn't fit listing in %v bytes", len(op.Dst))
   316  		}
   317  
   318  		op.BytesRead += n
   319  	}
   320  
   321  	return nil
   322  }